Now that we've discussed most of the basics we need for a C# program, let's talk about two concepts that are central to how C# (and indeed, all object-oriented programming languages) work: inheritance and polymorphism.

A closeup of a set of $100 bills being counted.
Wrong kind of inheritance! But this one is pretty nice... Photo by Pepi Stojanovski / Unsplash

The Sample Project

exceptionnotfound/CSharpInSimpleTerms
Contribute to exceptionnotfound/CSharpInSimpleTerms development by creating an account on GitHub.
Projects for this post: 9Inheritance and 9Polymorphism

Inheritance

Inheritance allows a class to reuse the properties, methods, and behavior of another class, and to extend or modify that behavior.

The class which implements the original properties or methods and will be inherited from is called the base class; the class which inherits from the base class is called the derived class. A derived class inherits from a base class.

"Is A" and "Is A Kind Of"

When talking about inheritance, we normally think of the derived classes having an "is a" or "is a kind of" relationship with the base class.

For example, a bee is an insect, a Toyota Corolla is a car, and a dresser is a kind of furniture. In these examples, Insect, Car, and Furniture are the base classes, while Bee, Toyota Corolla, and Dresser are the derived classes.

public class Insect { /*...*/ }
public class Bee : Insect { /*...*/ }

public class Car { /*...*/ }
public class ToyotaCorolla : Car { /*...*/ }

public class Furniture { /*...*/ }
public class Dresser : Furniture { /*...*/ }

In C#, we specify that an object inherits from another object using the : operator, as shown above.

You can have multiple distinct classes inherit from a common base class (this is, in fact, a defining trait of inheritance). In the code below, the derived classes Dog and Wolf both inherit from the base class Animal.

public class Animal //Base class
{
    public string SpeciesName { get; set; }
    public bool IsDomesticated { get; set; }

    public virtual void MakeSound() 
    {
        Console.WriteLine("Basic Animal Sound");
    }
}

public class Dog : Animal //Derived class
{
    public string BreedName { get; set; }

    public Dog(string breedName)
    {
        SpeciesName = "Canis familiaris";
        IsDomesticated = true;
        BreedName = breedName;
    }

    public override void MakeSound() 
    {
        Console.WriteLine("Bark!");
    }
}

public class Wolf : Animal //Derived class
{
    public Wolf()
    {
        SpeciesName = "Canis lupus";
        IsDomesticated = false;
    }

    public override void MakeSound() 
    {
        Console.WriteLine("Awooooooooo!");
    }
}

Elsewhere in our code, we can create instances of Dog and Wolf and call their respective MakeSound() methods.

Dog dog = new Dog("Labrador Retriever");
dog.MakeSound();

Wolf wolf = new Wolf();
wolf.MakeSound();

Access Modifiers

Properties and methods in the base class have access modifiers that change whether the derived classes can use them.

  • private members are not visible by derived classes.
  • internal members are only visible by derived classes if they are in the same assembly as the base class.
  • protected members are ONLY visible in derived classes.
  • public members are visible to all code.
public class Animal2
{
    private string SpeciesName;
    protected bool IsDomesticated { get; set; }
    public bool IsExtinct { get; set; }
}

public class Dodo : Animal2
{
    //We cannot access SpeciesName here, because it is private
    public Dodo()
    {
        IsDomesticated = false,
        IsExtinct = true
    }
}

var dodo = new Dodo();
dodo.IsDomesticated = true; //COMPILATION ERROR; IsDomesticated is protected
dodo.IsExtinct = true;
Remember that the term "member" can refer to a property, method, constructor, etc.

Overriding Methods

Derived classes can also change the implementation of the base class's methods. The derived class's implementation for said methods is used in place of the original implementation, which will not be executed.

In order to do this, the method in the base class must be marked with the virtual keyword, and the methods in the derived class must have the same name and parameters and be marked with the override keyword.

public class Animal
{
    public virtual void MakeSound() { /*...*/ }
}

public class Tiger : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Roar!");
    }
}

public class Hobbes : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Denial springs eternal.");
    }
}

Base Constructors

If the derived class's constructor needs to call a constructor in the base class, they can do so using the base keyword.

public class Animal
{
    public string SpeciesName { get; set; }
    public Animal(string speciesName)
    {
        SpeciesName = speciesName;
    }
}

public class BlueWhale : Animal
{
    public BlueWhale() : base("Balaenoptera musculus") { /*...*/ }
}

Implicit Inheritance

We know from the first post in this series that all C# classes inherit from a base type called System.Object. Due to inheritance, this also means that all objects ever instantiated in C# can all use methods defined in System.Object.

Let's define an new, empty Vegetable class and then create an instance of it to show what we mean:

public class Vegetable { }

Vegetable myVegetable = new Vegetable();
var stringDescription = myVegetable.ToString(); //Method implemented in System.Object
var type = myVegetable.GetType(); //Method implemented in System.Object

Because all objects in C# must have a type, and all objects in C# inherit from System.Object, the class Vegetable will be able to use methods defined on System.Object. This is called implicit inheritance.

All objects in C# can implement these common methods, including ToString(), GetType(), Equals(), GetHashCode(), MemberwiseClone() and more!

No Multiple Inheritance!

C# does not permit multiple inheritance; a single C# class cannot inherit from multiple other classes. There are other ways to inherit behavior, though, and the next post in this series will cover two of them: interfaces and abstract classes.

Polymorphism

Polymorphism, along with encapsulation and inheritance, are the three defining characteristics of object-oriented programming.

In short, polymorphism in C# means we can treat instances of a derived class as though they are instances of their base class. For example, let's define a base class and two derived classes, as well as a method which takes an instance of the base class as a parameter.

public class Animal //Base class
{
    public string SpeciesName { get; set; }
    public string CommonName { get; set; }
}

public class Elephant : Animal { /*...*/ } //Derived class

public class Porcupine : Animal //Derived class
{
    public string OrderType { get; set; } //Old world or New world
}

public static class StaticMethods
{
    public static string GetAnimalDetails(Animal animal) 
    {
        return animal.SpeciesName + "(AKA " + animal.CommonName + ")";
    }
}

If we instantiate an Elephant object and a Porcupine object, we can pass both of them to the GetAnimalDetails method and the method will treat each as though they are instances of the Animal class:

var porcupine = new Porcupine();
porcupine.OrderType = "New World";

var details = GetAnimalDetails(porcupine);

However, the method GetAnimalDetails() will not be able to access the property OrderType, because that property is defined in the derived class Porcupine and not in the base class Animal.

public void GetAnimalDetails(Animal animal)
{
    string type = animal.OrderType; //COMPILATION ERROR
    return;
}

Virtual Methods

We can work with polymorphism by implementing a C# feature that we saw in the Inheritance examples: virtual methods. These are methods defined on the base class that allow for the derived classes to implement some additional behavior, and optionally run the behavior defined in the base class.

We do this using the virtual keyword in the base class and the override keyword in the derived class:

public class Animal
{
    public virtual void Eat(string meal)
    {
        Console.WriteLine("Digesting " + meal);
    }
}

public class Fox : Animal
{
    public override void Eat(string meal)
    {
        Console.WriteLine("Chewing " + meal);
        base.Eat(meal);
    }
}

public class Anteater : Animal //They have no teeth!
{
    public override void Eat(string meal)
    {
        Console.WriteLine("Swallowing " + meal); 
        //NOTE: no call to base class here!
    }
}

var myFox = new Fox();
myFox.Eat("rabbit"); //Output: "Chewing rabbit"
                     //        "Digesting rabbit"

var myAnteater = new Anteater();
myAnteater.Eat("ants"); //Output: "Swallowing ants"

Virtual Properties

In addition to virtual methods, we can also have virtual properties on the base class that can be overridden by the derived classes.

public class Animal
{
    public virtual int YearDiscovered
    {
        get { return int.MaxValue; } //Unknown year
    }
}
public class VibraniumFairyWrasse : Animal
{
    public override int YearDiscovered
    {
        get { return 2019; }
    }
}
Yes, seriously. Species name: "Cirrhilabrus wakanda"!

Virtual methods and virtual properties allow us developers to extend the functionality of the base class without needing to use that base functionality.

Stopping Virtual Inheritance

It is possible to prevent derived classes from inheriting virtual methods. Let's see a new base class A and a derived class B:

public class A
{
    public virtual void GetDetails() 
    {
        Console.WriteLine("A.GetDetails invoked!");
    }
}

public class B : A
{
    public override void GetDetails()
    {
        Console.WriteLine("B.GetDetails invoked!");
        base.GetDetails();
    }
}

A derived class can stop virtual inheritance by declaring a member, method, or property as sealed (note that class C inherits from class A):

public class C : A
{
    public sealed override void GetDetails() 
    {
        Console.WriteLine("C.GetDetails invoked!");
        base.GetDetails();
    }
}

Which means that further-derived classes can no longer inherit the sealed method, property, or member:

public class D : C
{
    //We cannot override GetDetails()!
}

However, we can also implement a new version of the member, one whose definition is restricted to the class it was defined in, using the new keyword.

public class E : C
{
    public new void GetDetails()
    {
        Console.WriteLine("E.GetDetails invoked!");
    }
}

var myClassE = new E();
myClassE.GetDetails(); //Calls the implementation in Class E

var myClassC = new C();
myClassC.GetDetails(); //Calls the implementation in Class C, then Class A

Fun with Polymorphism

Polymorphism becomes particularly useful in, say, a method that can accept many types but does something different with each of them.

public class Animal { /*...*/ } //Base class

public class Elephant : Animal //Derived class
{
    public string ElephantType { get; set; } //African, Asian, Other?
}

public class Dolphin : Animal //Derived class
{
    public string DolphinType { get; set; } //Bottlenose, common, other?
}

public static class FunWithPolymorphism
{
    public static void OutputType(Animal animal)
    {
        if (animal.GetType() == typeof(Dolphin)) //GetType defined 
                                                 //on System.Object
        {
            var dolphin = (Dolphin)animal;
            Console.WriteLine(dolphin.DolphinType);
        }
        if (animal.GetType() == typeof(Elephant))
        {
            var elephant = (Elephant)animal;
            Console.WriteLine(elephant.ElephantType);
        }
    }
}

public static void Main(string[] args)
{
    var dolphin = new Dolphin()
    {
        DolphinType = "Bottlenose"
    };

    var elephant = new Elephant()
    {
        ElephantType = "Asian"
    };

    FunWithPolymorphism.OutputType(elephant);
    FunWithPolymorphism.OutputType(dolphin);

    Console.ReadLine();
}

Glossary

  • Inheritance - a property of object-oriented programming languages that allows for classes to adopt and use members, properties, methods, etc. defined in another class.
  • Base class - a C# class which is inherited by another class.
  • Derived class - a C# class which is inheriting from another class.
  • Multiple inheritance - where a single class inherits from multiple other classes. This is not permitted in C#; attempting to do this will cause a build error.
  • Polymorphism - a property of object-oriented programming languages that allows for a derived class to be treated as though it is an instance of its base class.
  • Virtual methods - Methods in a base class that can be overridden by that base's derived classes.
  • Virtual properties - Properties in a base class that can be overridden by that base's derived classes.

New Keywords

  • base - Used to call constructors in base classes.
  • override - Implements new or additional behavior
  • virtual - Specifies that a member, property, method, etc. can be overridden.
  • sealed - Specifies that a class or member cannot be inherited.

Summary

Inheritance is the ability for classes to use members from other classes. The class that implements the original behavior is called a base class, and the class that inherits from a base is called a derived class.

Polymorphism allows for instances of derived classes to be treated as though they are instances of their base class. We can allow for implementation in a base class to be extended using virtual methods and properties, which can optionally call the base implementation in addition to their implementation.

Got questions about inheritance or polymorphism? I don't blame you! These are complicated subjects that are difficult to describe thoroughly in a blog post. If you need help understanding these ideas, please ask questions in the comments below!

In the next part of this series, as mentioned earlier, we see and discuss other ways we can have C# classes implement behavior, properties, and methods. Specifically, we talk about interfaces, which define a common set of properties and methods for multiple classes, and abstract classes, which don't implement behavior but allow for derived classes to implement their own. Check it out!

C# in Simple Terms - Interfaces and Abstract Classes
Let’s inherit behavior and properties using two related, but different, kinds of C# objects.

Happy Coding!