Author: Brandon Pearman

The views expressed here are mine alone and do not reflect the view of my employer.

Overriding allows your derived class to change the behaviour inherited from the base class.

Overridding also falls under polymorphism since a single method can take on multiple forms depending on what it is cast to. Overridding is also called run-time polymorphism. This means the application can only determine at runtime which form of the function to run.

If you inherit from a class which has a normal method can you override that method? You can't really override it but you can hide it. Take a look at the below Animal class with its Talk() method.

Example:

public abstract class Animal
{
  public void Talk()
  {
        Console.WriteLine("...");
  }
}

public class Rat : Animal
{
    public new void Talk()
    {
        Console.WriteLine("Squeak");
    }
}

The base class "Animal" implements the Talk() method which the derived class "Rat" inherits but creates its own new Talk() method.

Base class methods with no added keywords can not be overridden by the derived class ie cannot use the keyword override. Instead the derived class can create their own version using the "new" keyword and hide the base classes method.

Test the override with the following:

public static void Main(string[] args)
{
    Animal animal = new Rat();
    animal.Talk(); // ...
    ((Rat)animal).Talk(); // Squeak

    Rat rat = new Rat();
    rat.Talk(); // Squeak
    ((Animal)rat).Talk(); // ...
}

The functionality of both methods still exist. The functionality which is used depends on the form of the object. eg if the object is in Animal form or Rat form.

No keyword methods summary:

Methods in the base class can be marked with the keyword "virtual". This will allow derived classes to partialy override with the "new" keyword like before but also allows it to fully override it with the keyword "override". Example:

public abstract class Animal
{
  public virtual void Poop()
  {
  Console.WriteLine("smelly poop");
  }

}

public class Rat : Animal
{

    public override void Poop()
    {
        Console.WriteLine("neat little ball of poop");
    }

}

Test the override with the following:

public static void Main(string[] args)
{
    Animal animal = new Rat();
    animal.Poop(); // neat little ball of poop
    ((Rat)animal).Poop(); // neat little ball of poop

    Rat rat = new Rat();
    rat.Poop(); // neat little ball of poop
    ((Animal)rat).Poop(); // neat little ball of poop
}

As you can see when a virtual method is overridden with the "override" keyword, the derived class method is always used regardless of the objects form. eg The Rat method will always be used, even if its cast into an Animal.

Virtual methods summary:

An abstract method does not have any implementation of it's own and forces the derived class to provide an implementation using the keyword "override". Note: can't use new. Abstract methods are similar in concept to an interface. Example:

public abstract class Animal
{
  public abstract void Eat();
}

public class Rat : Animal
{
    public override void Eat()
    {
        Console.WriteLine("Nom Nom Nom");
    }
}

Test the override with the following:

public static void Main(string[] args)
{
    Animal animal = new Rat();
    animal.Eat(); // Nom Nom Nom
    ((Rat)animal).Eat(); // Nom Nom Nom

    Rat rat = new Rat();
    rat.Eat(); // Nom Nom Nom
    ((Animal)rat).Eat(); // Nom Nom Nom
}

The Animal class has no other implementation, therefore regardless of the form of the object, it will always use the derived class's implementation.

Abstract methods summary:

All classes in C# implicitly inherit form the Object class. In Visual Studio, if you type out "Object" then go to its definition (F12), you will find all the methods it implements. You will also find some methods are marked with the "override" keyword, such a:

public virtual string ToString();
public virtual bool Equals(Object obj);
public virtual int GetHashCode();

ToString

C# does not know what information is relavant to convert your object into a string so it just prints out the object name. You can override the ToString method and return whatever string you would like in order to represent this object.

public class Car
{

  public string Model { get; private set; }
  public string Color { get; set; }

  public Car(string model, string color)
  {
      Model = model;
      Color = color;
  }

  public override string ToString()
  {
      return $"original ToString:{base.ToString()}, - Overriden ToString: This car is a {Color} {Model}";
  }

}

Equals

When you check if two objects are Equal then the default is to check reference equality which is generally not the behaviour you would want.

Rat rat1 = new Rat();
Rat rat2 = new Rat();
Console.WriteLine(rat1.Equals(rat2)); // false

Even though rat1 and rat2 are identical property wise, the Equals method returns false because the default behaviour of Equals is purely a reference check.

If you want a custom Equals() check you can simply override the Equals() method. Lets go through a basic example of how to do this.

  1. Check the reference since null's fail immediatly (for performance)
  2. Check the same reference since that passes immediatly (for performance)
  3. Compare the Type since objects of different Types are not equal (for performance)
  4. Then cast and check the specifics of the class
public class Car
{

  public string Model { get; private set; }
  public string Color { get; set; }

  public Car(string model, string color)
  {
      Model = model;
      Color = color;
  }

  public override bool Equals(Object obj)
  {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        if (obj.GetType() != this.GetType())
        {
            return false;
        }

        Car otherCar = obj as Car;
        if (otherCar == null)
        {
            return false;
        }

        return otherCar.Model.Equals(this.Model);
  }

}

Ee are not using the Color property to check equality, meaning a Red Tesla and a Blue Tesla will be considered equal.

Note: If you override Equals you will have to override GetHashCode as well, otherwise you will get "Override GetHashCode on overriding Equals". This is because if two objects are considered Equal then they should produce the same hashcodes.

GetHashCode

Let's start with "What on Earth is GetHashCode() used for?"... Hash codes are designed to put objects in a hash table. There are probably better ways to do this than a base method on every object but this is legacy of the CLR type system. The hash of the this object will be the hash of it's properties Model and Color

public class Car
{
  public string Model { get; set; }
  public string Color { get; set; }

  public Car(string model, string color)
  {
      Model = model;
      Color = color;
  }

  public override int GetHashCode()
  {
      var hashCode = -1388735503;
      hashCode = hashCode * -1521134295 + Model.GetHashCode();
      hashCode = hashCode * -1521134295 + Color.GetHashCode();
      return hashCode;
  }
}

Since there is already a good HashCode function in c# it's easier to use it.

public override int GetHashCode()
{
    // pre c# 7
    // GetHashCode of an Anonymous Type which creates an object on the heap (Garbage)
    return new { Model, Color }.GetHashCode();

    // c# 7
    // GetHashCode of a ValueTuple which executes on the stack (no Garbage)
    return (Model, Color).GetHashCode();
}

Check out these links for more info:

My C# language repo