Overloading and Local Functions

Overloading lets several methods share one name as long as their parameters differ, so callers can use the natural name whatever they pass in. Local functions are methods declared inside other methods, keeping small helpers right next to the only code that uses them.

Method overloading

Overloads have the same name but different parameter lists — a different number of parameters, or different types. The compiler picks the one that best matches the arguments. Overloads must be members of a class, so these live in a small static class:

C#
Console.WriteLine(Info.Describe(42));
Console.WriteLine(Info.Describe(3.14));
Console.WriteLine(Info.Describe("hello"));
Console.WriteLine(Info.Describe(2, 3));

static class Info
{
    public static string Describe(int n) => $"int {n}";
    public static string Describe(double d) => $"double {d}";
    public static string Describe(string s) => $"string of length {s.Length}";
    public static string Describe(int a, int b) => $"two ints, sum {a + b}";
}
Output
int 42
double 3.14
string of length 5
two ints, sum 5

You've already used overloads: Console.WriteLine has versions for int, double, string, bool and more, and Math.Round has versions with and without a number of decimals.

What counts as different

Only the parameter list matters. Two methods that differ only in their return type or parameter names conflict:

C#
static class Parser
{
    public static int Parse(string text) => int.Parse(text);
    public static double Parse(string text) => double.Parse(text);
}
Build error
Program.cs(4,26): error CS0111: Type 'Parser' already defines a member called 'Parse' with the same parameter types

Methods written directly after top-level statements are local functions (more on those below), and local functions can't be overloaded at all — two with the same name are always an error. That's why the overloading examples in this lesson put their methods inside a class.

Overload resolution

When several overloads could work, the compiler chooses the most specific one, preferring exact matches over conversions:

C#
Printer.Show(5);        // exact match: int
Printer.Show(5L);       // long
Printer.Show(5.0f);     // float has no overload: converted to double
Printer.Show('A');      // char converts to int

static class Printer
{
    public static void Show(int x) => Console.WriteLine($"int: {x}");
    public static void Show(long x) => Console.WriteLine($"long: {x}");
    public static void Show(double x) => Console.WriteLine($"double: {x}");
}
Output
int: 5
long: 5
double: 5
int: 65

If two overloads are equally good, the call is ambiguous and won't compile:

C#
Shipping.Cost(10, 10);

static class Shipping
{
    public static double Cost(int weight, double distance) => weight * distance;
    public static double Cost(double weight, int distance) => weight * distance;
}
Build error
Program.cs(1,10): error CS0121: The call is ambiguous between the following methods or properties: 'Shipping.Cost(int, double)' and 'Shipping.Cost(double, int)'

Overloads or optional parameters?

Both let callers supply fewer arguments. Optional parameters are simpler when the method body is the same and only a default changes. Overloads are better when the inputs are genuinely different types or need different logic:

C#
Console.WriteLine(Area.Of(3));            // circle
Console.WriteLine(Area.Of(3, 4));         // rectangle
Console.WriteLine(Area.Of([0, 0], [4, 0], [0, 3]));   // triangle from points

static class Area
{
    public static double Of(double radius) => Math.Round(Math.PI * radius * radius, 2);
    public static double Of(double width, double height) => width * height;
    public static double Of(double[] a, double[] b, double[] c) =>
        Math.Abs((b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1])) / 2;
}
Output
28.27
12
6

Local functions

A local function is declared inside another method and can only be called from there. It's ideal for a helper that only makes sense in one place. (The methods you've written after top-level statements are, in fact, local functions of the hidden Main.)

C#
Console.WriteLine(Report.Build([3, 8, 12, 5]));

static class Report
{
    public static string Build(int[] values)
    {
        return $"min {Format(values.Min())}, max {Format(values.Max())}";

        static string Format(int n) => n < 10 ? $"0{n}" : n.ToString();
    }
}
Output
min 03, max 12

Local functions can be declared after the return statement, keeping the main logic at the top.

Capturing variables

A non-static local function can use the variables of the method around it. Marking it static forbids that, which documents that it's self-contained and avoids accidental dependencies:

C#
int threshold = 50;
int[] readings = [12, 75, 49, 88, 50];

foreach (int r in readings)
{
    if (IsHigh(r))
        Console.WriteLine($"{r} is above {threshold}");
}

bool IsHigh(int value) => value > threshold;   // captures 'threshold'
Output
75 is above 50
88 is above 50
C#
int threshold = 50;
Console.WriteLine(IsHigh(60));

static bool IsHigh(int value) => value > threshold;
Build error
Program.cs(4,42): error CS8421: A static local function cannot contain a reference to 'threshold'.

Local functions for validation

A common use is separating argument checks from the main work, with the helper doing the real computation:

C#
Console.WriteLine(MathUtil.Power(2, 10));
Console.WriteLine(MathUtil.Power(3, 0));

static class MathUtil
{
    public static long Power(long baseValue, int exponent)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(exponent);
        return Compute(baseValue, exponent);

        static long Compute(long b, int e)
        {
            long result = 1;
            for (int i = 0; i < e; i++) result *= b;
            return result;
        }
    }
}
Output
1024
1

ArgumentOutOfRangeException.ThrowIfNegative is one of several built-in guard helpers; lesson 52 shows more.

Try it yourself

Add an overload of Repeat that takes a char and a separator string.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Output
ababab
*****