Overloading

Several methods can share one name as long as their parameter lists differ; the compiler picks the one that fits the arguments at each call.

Syntaxint Area(int side) and int Area(int width, int height) // one name, different parameter lists

Overloading means declaring more than one method with the same name in the same type, each with a different parameter list. It exists so that one idea needs only one name. Console.WriteLine is overloaded many times over: you call it with an int, a string or a bool and it is always the same name.

What counts as different

A method's signature is its name plus the types and modifiers of its parameters. The return type is not part of the signature for overloading. Two methods whose parameter lists match but whose return types differ do not compile; that is error CS0111. Nor can two overloads differ only by ref, out, in or ref readonly on the same parameter type. A ref parameter versus the same type passed by value is a legal pair.

How one is chosen

The compiler collects every candidate whose parameters can accept the arguments given, then picks the best fit. An exact type match beats one that needs a conversion. If two candidates are equally good the call is ambiguous and fails with CS0121. If two are equally good and one has optional parameters that were omitted, the one without them wins.

What trips people up

Overload choice is made at compile time from the declared types of the arguments, not from what the values are at run time. A long argument with no long overload quietly goes to the double one. When overloads do different things, that silent conversion is a bug.

Example

Example · csharp
Console.WriteLine(Text.Describe(7));      // exact match on int
Console.WriteLine(Text.Describe(7.0));    // exact match on double
Console.WriteLine(Text.Describe("7"));    // exact match on string

// There is no long overload. long converts to double, so that one is chosen,
// and 7.0 prints as 7.
Console.WriteLine(Text.Describe(7L));

// Output:
// int: 7
// double: 7
// string: 7
// double: 7

// Overloads have to be members of a type. Two local functions cannot share a
// name, so these live in a class rather than next to the statements above.
static class Text
{
    public static string Describe(int count)
    {
        return $"int: {count}";
    }

    public static string Describe(double amount)
    {
        return $"double: {amount}";
    }

    public static string Describe(string text)
    {
        return $"string: {text}";
    }
}

When to use it

  • Accept either a string id or an int id without making callers convert first.
  • Offer a two-argument convenience version of a method that really takes five.
  • Give a formatting method one name whether it is handed a number, a date or text.

More examples

Legal and illegal differences

Shows the line between a valid overload and CS0111. When you want the same name to produce a different type of result, you need a different name, such as HasText, or a different parameter list.

Example · csharp
Console.WriteLine(Measurer.Measure("two words"));
Console.WriteLine(Measurer.Measure("two words", true));

// Output:
// 9
// 8

static class Measurer
{
    public static int Measure(string text)
    {
        return text.Length;
    }

    // A different parameter list is a legal overload.
    public static int Measure(string text, bool ignoreSpaces)
    {
        return ignoreSpaces ? text.Replace(" ", "").Length : text.Length;
    }

    // Not legal. Same name, same parameter list, only the return type differs.
    // Error CS0111: the type already defines a member called 'Measure'
    // with the same parameter types.
    // public static bool Measure(string text)
    // {
    //     return text.Length > 0;
    // }
}

An overload beats an omitted default

Useful to know because mixing overloads and optional parameters for the same name makes the chosen method hard to predict by eye. Pick one technique per method name.

Example · csharp
// Both candidates fit. When two are equally good, the one with no omitted
// optional parameters wins.
Logger.Log("one");
Logger.Log("two", "billing");

// Output:
// [default] one
// [billing] two

static class Logger
{
    public static void Log(string message)
    {
        Console.WriteLine($"[default] {message}");
    }

    public static void Log(string message, string category = "general")
    {
        Console.WriteLine($"[{category}] {message}");
    }
}

Overloads that forward to one real implementation

This is the shape most overload sets should have: the small ones fill in values and call the big one. It keeps the behaviour in a single place, which matters once the real method is longer than two lines.

Example · csharp
Console.WriteLine(Joiner.Join("left", "right"));
Console.WriteLine(Joiner.Join("left", "right", "-"));

// Output:
// left right
// left-right

static class Joiner
{
    // The short overload exists for convenience. Only one contains logic.
    public static string Join(string a, string b)
    {
        return Join(a, b, " ");
    }

    public static string Join(string a, string b, string separator)
    {
        return a + separator + b;
    }
}

Discussion

  • Be the first to comment on this lesson.