# Method overloads

> 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.

<!-- id: class-overloads · area: class · stability: stable · html: https://osysharp.com/reference/class/overloads/ -->

## Summary        {#summary}
Two methods, one name, different parameter types:

```osy title="an overload set" test app=class-overloads
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:

```osy title="each call runs its own body" run app=class-overloads
[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      {#signature}
```osy syntax
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    {#description}

### What makes two overloads different?   {#what-differs}
**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:

```osy syntax
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?   {#how-a-call-picks}
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](https://osysharp.com/reference/class/methods/), which make a parameter optional, and named arguments, which bind by name rather
than position. This is usually the whole story:

```osy title="chosen by argument count" run app=class-overloads
[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:

```osy title="chosen by argument type" test app=class-overloads
class Formatter {
  public string Show(decimal d) { return "number"; }
  public string Show(string s) { return "text"; }
}
```

```osy title="the type of the argument decides" run app=class-overloads
[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`:

```osy title="the more specific overload wins" test app=class-overloads
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"; }
}
```

```osy title="a Circle picks Draw(Circle)" run app=class-overloads
[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   {#static-not-virtual}
This is the one rule worth reading twice, because it differs from how [`override`](https://osysharp.com/reference/class/inheritance/) 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.

```osy title="the slot decides, not the value" run app=class-overloads
[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](https://osysharp.com/reference/class/inheritance/).

### Can a subclass add an overload?   {#inheritance}
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`):

```osy title="one adds, one overrides" test app=class-overloads
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
}
```

```osy title="both are callable, and the override still dispatches" run app=class-overloads
[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   {#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:

```osy syntax
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   {#properties}
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   {#constructors}
A class declares one [constructor](https://osysharp.com/reference/class/constructors/). Give it the widest parameter list and default the ones a
caller may omit:

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

## See also       {#see-also}
- [class methods](https://osysharp.com/reference/class/methods/) — declaring methods, default values, and named arguments
- [Class inheritance](https://osysharp.com/reference/class/inheritance/) — `virtual`/`override`, and why they decide at a different moment than overloads do
- [constructor](https://osysharp.com/reference/class/constructors/) — the single constructor, and defaulting its parameters
- [Classes](https://osysharp.com/reference/class/index/) — what a class is
