Method Overloading


Two methods of a class may share a name provided that their parameter declarations differ. When this is the case, the methods are said to be overloaded. The next example shows a class with multiple overloads of the same method name.

// Overload1 - A Demonstration of Method Overloading

using System;

class Overload
{
    public void Overloaded()
    {
        Console.WriteLine("Called Overloaded with no parameters");
    }

    public void Overloaded(int i)
    {
        Console.WriteLine("Called Overloaded with 1 integer parameter {0}", i);
    }

    public void Overloaded(double d)
    {
        Console.WriteLine("Called Overloaded with 1 double parameter {0}", d);
    }

    public void Overloaded(double x, double y)
    {
        Console.WriteLine("Called Overloaded with 2 double parameters {0} & {1}", x, y);
    }

    public void Overloaded(string s)
    {
        Console.WriteLine("Called Overloaded with string parameter \"{0}\"", s);
    }
}


class OverloadDemo
{
    static void Main()
    {
        Overload o = new Overload();

        o.Overloaded();
        o.Overloaded(1);
        o.Overloaded(1.5);
        o.Overloaded(1.5,2.5);
        o.Overloaded("Hello World");
    }
}

The output of the program is shown below.

Called Overloaded with no parameters
Called Overloaded with 1 integer parameter 1
Called Overloaded with 1 double parameter 1.5
Called Overloaded with 2 double parameters 1.5 & 2.5
Called Overloaded with string parameter "Hello World"

The class under consideration is called Overload. It has 5 methods called Overloaded. One has no parameters, one has one integer parameter, one has one floating point parameter, another has two floating point parameters and finally one has a string parameter.

In C#, each method of each class has an internal name. The compiler is able to distinguish between methods with the same name because it encodes type information into the internal name of the method (as well as the class name). Each of the above methods is said to have a different signature. A signature is the name of the function together with the type information of its parameters. The signatures are as follows.

Overload.Overloaded();
Overload.Overloaded(int);
Overload.Overloaded(double);
Overload.Overloaded(double,double);
Overload.Overloaded(string);

The full signature contributes to the internal name (the process is referred to as name mangling). So while just the method names match, the internal (mangled) names are quite different and no confusion can arise. Note that the return type does not contribute to the signature of the method and is not able to be used to differentiate between otherwise identical methods. Methods can however differ in the return type and still overload the same name provided that their signatures are different.

Type Conversions When Overloading

As previously discussed, C# provides type conversions. These conversions also apply to the parameters of overloaded methods when attempting to resolve calls. For example, consider the following program.

// Overload2 - Type Conversions and Overloading

using System;

class X
{
    public void f(int i)
    {
        Console.WriteLine("Inside f(int) with parameter = {0}", i );
    }

    public void f(double d)
    {
        Console.WriteLine("Inside f(double) with parameter = {0}", d);
    }
}

class Program
{
    static void Main()
    {
        X x = new X();

        int i = 100;
        double d = 100.5;
        byte b = 127;
        short s = 20;
        float f = 20.5f;

        x.f(i); // calls x.f(int)
        x.f(d); // calls x.f(double)

        x.f(b); // calls x.f(int) - with type conversion
        x.f(s); // calls x.f(int) - with type conversion
        x.f(f); // calls x.f(double) - with type conversion
    }
}

The output of the program is shown below.

Inside f(int) with parameter = 100
Inside f(double) with parameter = 100.5
Inside f(int) with parameter = 127
Inside f(int) with parameter = 20
Inside f(double) with parameter = 20.5

From the output it is clear that conversions are taking placing before function resolution is being conducted. For example, the byte b is first promoted to integer so that the call to f(int) can be matched and made.

Automatic type conversions take place only when no direct match is found. For example, if the previous program is updated to contain an f(byte) method, that method will be called in preference to a type conversion taking place. Here is the updated program.

// Overload3 - Type Conversions and Overloading

using System;

class X
{
    public void f(byte b)
    {
        Console.WriteLine("Inside f(byte) with parameter = {0}", b);
    }
    
    public void f(int i)
    {
        Console.WriteLine("Inside f(int) with parameter = {0}", i);
    }

    public void f(double d)
    {
        Console.WriteLine("Inside f(double) with parameter = {0}", d);
    }
}

class Program
{
    static void Main()
    {
        X x = new X();

        int i = 100;
        double d = 100.5;
        byte b = 127;
        short s = 20;
        float f = 20.5f;

        x.f(i); // calls x.f(int)
        x.f(d); // calls x.f(double)

        x.f(b); // calls x.f(byte)
        x.f(s); // calls x.f(int) - with type conversion
        x.f(f); // calls x.f(double) - with type conversion
    }
}

The updated output is shown below.

Inside f(int) with parameter = 100
Inside f(double) with parameter = 100.5
Inside f(byte) with parameter = 127
Inside f(int) with parameter = 20
Inside f(double) with parameter = 20.5

When a byte was supplied as a parameter, an exact match occurred and the appropriate method was called; whereas, in the previous program a conversion then match took place.

Both ref and out participate in the signature and therefore overloading resolution. The following methods are distinct.

class X
{
 void f(int i)     {...}
 void f(ref int i) {...}
}

The following code fragment shows the different means of invocation.

X object = new X();
int i=10;

object.f(i);       // invokes X.f(int)
object.f(ref i);   // invokes X.f(ref int)

Although ref and out participate in overloading resolution, they are not sufficiently different to distinguish one from the other. For example, the following is not possible.

class X
{
 void f(out int i) {...}  // Error: ref and out not sufficiently different
 void f(ref int i) {...}
}

Note also that params does not participate in the signature of a method therefore it does not participate in overloading resolution.

Overloading Constructors

Constructors can be and quite frequently are overloaded. For example, a point class may have three constructors as follows.

class Point
{
    public double x;
    public double y;

    public Point()
    {
        x = 0;
        y = 0;
    }

    public Point(double xSet, double ySet)
    {
        x = xSet;
        y = ySet;
    }

    public Point(Point copy)
    {
        x = copy.x;
        y = copy.y;
    }
}

The first constructor is the default constructor and it sets the coordinates of the point to zero. The second constructor requires two input values and it initializes the coordinates of the point with the given values. The third constructor is the copy constructor. It replicates a point. The third constructor would be invoked as follows.

Point pt = new Point(20,30);
Point ptCopy = new Point(pt);  // invoke the copy constructor

On occasions, a constructor can call another constructor. This is accomplished through another form of the this keyword. The required general form is shown below.

constructor_name(parameter_list1)
 : this(parameter_list2)
{
 ...
}

For example, the above point class can be rewritten so that one constructor calls another, as shown below.

// Point6 - Constructors - Overloading

using System;

class Point
{
    public double x;
    public double y;

    public Point()
    {
        x = 0;
        y = 0;
    }

    public Point(double xSet, double ySet)
    {
        x = xSet;
        y = ySet;
    }

    public Point(Point copy)             // calls other constructor
        : this(copy.x, copy.y) { }

    public override string ToString()
    {
        return "(" + x + "," + y + ")";
    }
}

class PointDemo
{
    static void Main()
    {
        Point p = new Point(3, 4);
        Point q = new Point(p);

        Console.WriteLine("Copy of {0} is {1}", p, q);

    }
}

The output of the program is as follows.

Copy of (3,4) is (3,4)

An advanced touch has been added in the form of a ToString method, which allows for simplified output. This technique will be covered in detail later.