Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Class

Method overloads

<T> <Name>(<params>) { … } <T> <Name>(<other params>) { … } // same name, different parameter TYPES

A class can declare several methods with the same name, as long as they differ in their parameter types. Each call picks the one that fits its arguments — by how many, then by their types, then by which parameter type is most specific. A different return type or different parameter names is not a difference, and two methods that differ only that way are a compile error.

stable10 examples compiled by CIclassmethodsoverloading

Summary#

Two methods, one name, different parameter types:

class Calc {
  public decimal Add(decimal a) {
    return a;
  }

  public decimal Add(decimal a, decimal b) {
    return a + b;
  }
}

Each call site picks the one its arguments fit:

[Test]
void Each_Call_Picks_Its_Overload() {
  var c = new Calc();
  Assert.Equal(1m, c.Add(1m));
  Assert.Equal(3m, c.Add(1m, 2m));
}

Signature#

class C {
  T M(A a) { … }          // one overload
  T M(A a, B b) { … }     // another — different parameter COUNT
  T M(B b) { … }          // another — different parameter TYPE
}

Description#

What makes two overloads different?#

Their parameter types, in order — and nothing else. Two methods that differ only in return type, or only in parameter names, are the same method declared twice, and that is a compile error:

decimal Add(decimal a) { … }
string  Add(decimal b) { … }   // ✗ same signature — the return type and the name `b` are not differences

The reason is that a call site cannot act on either one. c.Add(1m) says nothing about what it wants back, and it does not name the parameter, so there would be no way to say which you meant.

Which overload does a call pick?#

In three steps, stopping as soon as one candidate is left.

1. How many arguments. Only the overloads that your arguments can bind to survive — counting default values, which make a parameter optional, and named arguments, which bind by name rather than position. This is usually the whole story:

[Test]
void Arity_Decides() {
  var c = new Calc();
  Assert.Equal(5m, c.Add(5m));        // the one-parameter Add
  Assert.Equal(9m, c.Add(4m, 5m));    // the two-parameter Add
}

2. What type they are. When several overloads take the right number of arguments, the ones whose parameters your arguments actually fit survive:

class Formatter {
  public string Show(decimal d) { return "number"; }
  public string Show(string s) { return "text"; }
}
[Test]
void Type_Decides() {
  var f = new Formatter();
  Assert.Equal("number", f.Show(1m));
  Assert.Equal("text", f.Show("x"));
}

3. Which is most specific. If more than one still fits, the one whose parameter type is lower in the class hierarchy wins — Circle beats Shape:

class Shape { public string Name; }
class Circle : Shape { public decimal Radius; }

class Painter {
  public string Draw(Shape s) { return "shape"; }
  public string Draw(Circle c) { return "circle"; }
}
[Test]
void Most_Specific_Wins() {
  var p = new Painter();
  Assert.Equal("circle", p.Draw(new Circle { Name = "a", Radius = 1m }));
}

It is the DECLARED type that chooses, not the runtime one#

This is the one rule worth reading twice, because it differs from how override works. Which overload runs is decided when your code is compiled, from the type of the variable you are holding. Which override runs is decided while it runs, from the type of the object.

[Test]
void The_Declared_Type_Chooses() {
  var p = new Painter();
  Shape held = new Circle { Name = "a", Radius = 1m };
  Assert.Equal("shape", p.Draw(held));    // held is declared `Shape` — even though it holds a Circle
}

If you want the object to decide, that is what virtual/override is for — see Class inheritance.

Can a subclass add an overload?#

A subclass inherits its base's overloads and can add to the set. Declaring a method with a new signature adds an overload; declaring one with an existing signature overrides it (and must say override):

class Reporter {
  public virtual string Line(decimal a) { return "base-1"; }
}

class RichReporter : Reporter {
  public override string Line(decimal a) { return "rich-1"; }      // same signature → an override
  public string Line(decimal a, decimal b) { return "rich-2"; }    // new signature → a sibling overload
}
[Test]
void Adding_And_Overriding() {
  var r = new RichReporter();
  Assert.Equal("rich-1", r.Line(1m));
  Assert.Equal("rich-2", r.Line(1m, 2m));

  Reporter asBase = r;
  Assert.Equal("rich-1", asBase.Line(1m));    // virtual dispatch still finds the override
}

base.Line(…) picks from the base's set the same way, so an override of one overload can still call either.

When a call is ambiguous#

If two overloads both fit and neither is more specific, the call is refused rather than guessed. Say which you mean by giving the argument a declared type:

class P {
  string Go(Left? l, Right? r) { … }
  string Go(Right? r, Left? l) { … }
}

p.Go(null, null);        // ✗ ambiguous — both fit, neither is more specific
Left? l = null;
p.Go(l, null);           // ✓ the declared type of `l` settles it

A method and a property cannot share a name#

Only methods overload. A property has no overload set to join, so a method named after a property is refused — one of the two has to be renamed.

Constructors do not overload yet#

A class declares one constructor. Give it the widest parameter list and default the ones a caller may omit:

public Box(decimal width, decimal height = 1m) { … }   // one constructor, two ways to call it

See also#

  • class methods — declaring methods, default values, and named arguments
  • Class inheritancevirtual/override, and why they decide at a different moment than overloads do
  • constructor — the single constructor, and defaulting its parameters
  • Classes — what a class is

Related

class methods

Behaviour attached to a class — a method with a receiver, called as value.Method(). Classes are in-memory values, so a…

Class inheritance

A class can derive from another class with `class Circle : Shape`, inheriting its fields and its methods to any depth…

constructor

A class declares one constructor — its name is the class name, it takes no return type, and it runs when you write new…

Classes

A class is an in-memory shape — data plus the behaviour that belongs to it — and it never touches the database. That is…