Overriding versus Overloading

Two very important concepts in object-oriented programming are overriding and overloading. Overloading is about creating multiple methods with the same name, but different signatures, in the same scope. Overriding is about changing the behavior of a certain method in the child class from the way it is behaving in the parent class. The rest of this article will explore these two concepts in detail.

Overloading occurs when a method has more than one definition in the same scope. It’s important to remember two key points from the previous statement: same name and same scope. The method implementations have the same name because they do similar tasks. For instance, if we need to implement a method that gets the student name, there are many ways to do that. We can get the name using an id and we can get the name using a social security number. One way to implement the methods is as follows:


  class   Student

{

public   string GetName( int id) {…}

public   string GetName( string ssn) {…}

}


The method GetName() is overloaded since the two implementations are in the same scope (class scope) and have the same name. If the methods are declared in different scopes (for example, different classes), then we are not talking about overloading.

The first question that comes to mind is: do we have to declare them with the same name? The answer is no. We could have named them GetName1() and GetName2(), or even GetNameById() and GetNameBySSN(). So overloading is not mandatory; it is just a helpful feature in object-oriented languages (like C++, Java, and C#). It is common sense since both methods are doing pretty much the same thing.

Because the methods have the same name, the compiler will use the signature to determine what method to call under different scenarios. The compiler will be able to tell the difference using the method signature, which has to do with the method parameters. Be definition, the method signature is the name of the method and its parameters. Since the name in this case is the same, what is left is the parameters. There are three ways to distinguish one set of parameters from the other:

  1. Parameter count - one method could have 2 parameters while the other has 3. This way, the compiler will call the method with the correct number of parameters.

  2. Parameter type – if two methods have the same parameter count, but different types (int, string, long …), then it is easy for the compiler to know which method to call.

  3. Parameter order – when we have a match in the parameter count and parameter type, but with a different order, then it is again easy for the compiler to know which method to call. For example:


public   string GetName( int id, string match) {…}

public   string GetName( string match, int id) {…}

{mospagebreak title=Method Overloading Example}

Here is a full example that illustrates method overloading:


namespace OverloadingMethods

{

class   Program

{

static   void Main( string [] args)

{

Point p1 = new   Point ();

Console .WriteLine( "p1 -> {0}" , p1);

p1.Move();

Console .WriteLine( "p1 -> {0}" , p1);

p1.Move(2);

Console .WriteLine( "p1 -> {0}" , p1);

p1.Move(4, 7);

Console .WriteLine( "p1 -> {0}" , p1);


Point p2 = new   Point (2, 5);

Console .WriteLine( "p2 -> {0}" , p2);

p2.Move(10, 9);

Console .WriteLine( "p2 -> {0}" , p2);

p2.Move();

Console .WriteLine( "p2 -> {0}" , p2);

}

}


class   Point

{

private   int x;

private   int y;

Random rand = new   Random ();


public Point()

{

x = y = 0;

}


public Point( int x_value, int y_value)

{

x = x_value;

y = y_value;

}


//Move x with a random number between 1 and 100 (inclusive)

//Move y with a random number between 1 and 50 (inclusive)

public   void Move()

{

x += rand.Next(1, 101);

y += rand.Next(1, 51);

}


//move x and y by the same number (delta)

public   void Move( int delta)

{

x += delta;

y += delta;

}


//move x and y by the specified values

public   void Move( int delta_x, int delta_y)

{

x += delta_x;

y += delta_y;

}


public   override   string ToString()

{

return   string .Format( "[{0},{1}]" , x, y);

}

}

}


Notice that the Point class is flexible in the way a point can be moved. By overloading the Move() method, we are able to move a point via different x and y values, via the same value (delta), or via random x and y values. This way, our class gives its user different ways of moving a point using the same friendly Move() method.

{mospagebreak title=Overload a Constructor}

In addition to overloading a method, we are also able to overload a constructor. In the same way we give the user different ways of calling a method, we can provide different ways to construct an object. In the Point class, we did just that when we created two constructors: a default constructor (no parameters) and a constructor that takes x and y values. Here is another example that illustrates the way we can overload a constructor:


namespace Overloading

{

class   Program

{

static   void Main( string [] args)

{

Fraction f1 = new   Fraction ();

Console .WriteLine(f1.ToString());

Fraction f2 = new   Fraction (3);

Console .WriteLine(f2.ToString());

Fraction f3 = new   Fraction (2, 5);

Console .WriteLine(f3.ToString());

}

}


class   Fraction

{

private   int num;

private   int denom;


//constructors

public Fraction()

{

num = denom = 1;

}


public Fraction( int n)

{

num = n;

denom = 1;

}


public Fraction( int n, int d)

{

num = n;

denom = d;

}


public   override   string ToString()

{

return   string .Format( "Fraction {0}/{1} = {2}" ,

num, denom, ( double )num / denom);

}

}

}


In the Fraction class, we provided three different constructors:

  1. public Fraction(): This is the default constructor. It allows the user to create the simplest fraction, 1 for the numerator and 1 for the denominator (1/1).

  2. public Fraction( int n): This constructor is similar to the previous one, but it allows the user to set the numerator (denominator still 1).

  3. public Fraction( int n, int d): This constructor gives the fullest flexibility because it allows the user to provide values for the numerator and the denominator.

Having these three constructors, now we can create different fractions based on our needs:


Fraction f1 = new   Fraction ();

Fraction f2 = new   Fraction (3);

Fraction f3 = new   Fraction (2, 5);


Final note: The return type method cannot be used in the method signature to distinguish one method call from the other. So the following is not considered overloading; it is actually a compile time error:


public   void Move( int delta_x, int delta_y) {…}

public   int Move( int delta_x, int delta_y) {…}


The only difference between the two methods is that one returns a void, and the other returns an int.

{mospagebreak title=Overriding}

As mentioned earlier, overriding has to do with parent and child classes. If you are not satisfied with the implementation of a method in the parent class, you can keep the same declaration (signature and return type), but provide a different implementation. For example, a Rectangle class inherits the drawing functionality from a Shape class, but it overrides this functionality in order to be able to draw a rectangle.

We have been using overriding all along in our examples. In C#, everything is an object, so when we create a class ( Employee , Fraction …) that does not specifically inherit from another class, it automatically inherits the functionality of an Object. Notice that in our previous examples, we were able to use the ToString() method to print an object as a string. The ToString() method is defined in the Object class. Since the Object class is too general, this method cannot give us anything useful, so we override it.

Ponder the following example:


namespace OverridingExample

{

class   Program

{

static   void Main( string [] args)

{

Employee e = new   Employee ( "Jim" , "Tester" , 38, 15.95);

Console .WriteLine(e);

Manager m = new   Manager ( "Jennifer" , "Jones" , 40, 25, 350);

Console .WriteLine(m);

}

}


class   Employee

{

protected   string firstName;

protected   string lastName;

protected   int hoursWorked;

protected   double ratePerHour;


public Employee( string f_name, string l_name,

int hours, double rate)

{

firstName = f_name;

lastName = l_name;

hoursWorked = hours;

ratePerHour = rate;

}


public   virtual   double CalculatePay()

{

return hoursWorked * ratePerHour;

}


public   override   string ToString()

{

return   string .Format( "Name: {0} {1}nPay: {2}" ,

firstName, lastName, CalculatePay());

}

}


class   Manager : Employee   //Manager inherits from Employee

{

private   double bonus;


public Manager( string f_name, string l_name,

int hours, double rate, double bonus)

: base (f_name, l_name, hours, rate)

{

this .bonus = bonus;

}


public   override   double CalculatePay()

{

return   base .CalculatePay() + bonus;

}

}

}


In the Employee class, we are overriding the ToString() method in order to print the Employee in a nicely formatted string (notice the keyword override in the method declaration). The Employee class did not specifically inherit from any class. So by default, it inherits from the Object class, and we are able to override the ToString() method. The CalculatePay() method calculates the Employee ’s pay (rate * hoursWorked).

In the Manager class, on the other hand, we did not override the ToString() method because the one inherited from the Employee class is sufficient. But a Manager’s pay is different from a regular Employee’s pay. Based on that, we can override the CalculatePay() method to account for the bonus. To make this work, however, we need to declare the CalculatePay() method in the Employee class as virtual (which means it is able to be overridden). Now, when we have a reference pointing to an Employee class, it will call that Employee’s CalculatePay() method. When that reference is pointing to a Manager class, it will call the Manager’s Calculatepay() method.

Conclusion

Overriding and overloading are very important features in an object-oriented language like C#. When used correctly, they can lead to very powerful and readable code. When we have methods with similar functionality, it is not necessary to give them different names when we can overload them instead. And when we are not satisfied with the functionality provided in a parent class, we can override the method to meet our needs.

3 thoughts on “Overriding versus Overloading

  1. Welcome to my latest article on overriding versus overloading.
    I am looking forward to your comments and thoughts.

  2. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace FunctionOverLoading
    {
    ///

    /// It accept the month entered by the user and display the number of days in it
    ///

    class DayCount
    {
    int Days;
    int Month;
    int Year;

    //Constructor to initialize month and year
    public DayCount()
    {
    Month = 0;
    Year = 0;
    }

    //Return 1 if year is a leap year, else returns 0
    public Boolean LeapYear()
    {
    if (Year % 4 == 0)

    return true;
    else
    return false;
    }

    //Sets the month and year
    public void SetDate(int Month1, int Year1)
    {
    Month=Month1;
    Year=Year1;
    }

    //Sets the month, overloaded function
    public void SetDate(int Month1)
    {
    Month=Month1;

    }

    //Returns the name of the month
    public string MonthName()
    {
    switch (Month)
    {
    case 1: return “January”;
    //Console.WriteLine(“January”);
    //break;

    case 2: return “February”;
    //Console.WriteLine(“February”);
    //break;

    case 3: return “March”;
    //Console.WriteLine(“March”);
    //break;

    case 4: return “April”;
    //Console.WriteLine(“April”);
    //break;

    case 5: return “May”;
    //Console.WriteLine(“May”);
    //break;

    case 6: return “June”;
    //Console.WriteLine(“June”);
    //break;

    case 7: return “July”;
    //Console.WriteLine(“July”);
    //break;

    case 8: return “August”;
    //Console.WriteLine(“August”);
    //break;

    case 9: return “September”;
    //Console.WriteLine(“September”);
    //break;

    case 10: return “October”;
    //Console.WriteLine(“October”);
    //break;

    case 11: return “November”;
    //Console.WriteLine(“November”);
    //break;

    case 12: return “December”;
    //Console.WriteLine(“December”);
    //break;

    default: return “Invalid Month Entered”;
    //break;

    }
    }

    //Sets the number of days in a month
    public void SetDays()
    {
    switch(Month)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
    {
    Days=31;
    break;
    }
    case 2:
    {
    Days=28;
    if (LeapYear())
    Days = 29;//Assign 29 days if leap year
    break;
    }
    case 4:
    case 6:
    case 9:
    case 11:
    {
    Days=30;
    break;
    }
    }
    }

    public void Display()
    {
    string[] name = new string[25];
    SetDays();
    Console.WriteLine(“The number of days in a month of ” + MonthName() + ” is ” + Days);
    //Console.WriteLine(” is ” + Days);
    //Console.Write(“is “+ Days);

    }

    static void Main(string[] args)
    {
    DayCount dayCount = new DayCount();
    int Month, Year;
    Console.WriteLine(“Enter the month in number:”);
    Month = Convert.ToInt32(Console.ReadLine());
    if (Month > 12)
    {
    Console.WriteLine(“Invalid Month Specified!”);
    }
    else
    {

    //dayCount.SetDate(Month);
    Console.WriteLine(“Enter the year:”);
    Year = Convert.ToInt32(Console.ReadLine());
    dayCount.SetDate(Month, Year);
    dayCount.Display();
    }
    Console.ReadLine();
    }

    }

    /*class Collection
    {
    ///

    /// It executes three functions with same name with different parameters
    ///

    /// /// This Display function will accept a string and display a string.
    public static void Display(string FirstName)
    {
    Console.WriteLine(FirstName);

    }

    //This Display function will accept an integer and display a integer.
    public static void Display(int number)
    {
    Console.WriteLine(number);
    }

    //This Display function will accept an character and display a character.
    public static void Display(char c)
    {
    Console.WriteLine(c);
    }

    static void Main(string[] args)
    {
    Display(“Irfan”);
    Display(1234);
    Display(‘A’);
    Console.ReadLine();
    }
    }*/

    }

[gp-comments width="770" linklove="off" ]