65. Inheritance#

Inheritance, in object oriented languages, gives us two things:

  1. Code reuse: It allows us to re-use code from the superclass in the subclass. We say that the subclass inherits the members of the superclass.

  2. Subtype polymorphism: It makes the subclass substitutable for the superclass. We say that the subclass inherits the type of the superclass.

Warning

If you don’t need both of these things, then don’t use inheritance. If you want code reuse, then use composition. If you want subtype polymorphism, then use interfaces.

Note

The terms ‘parent class’ and ‘base class’ are synonyms of the term ‘superclass’. The terms ‘child class’ and ‘derived class’ are synonyms of the term ‘subclass’.

Inheritance is a binary relationship where one class inherits its members and type from another. Consider the following example:

public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}
public class Dog : Animal
{
    public void Bark()
        => Console.WriteLine($"{Name} is barking.");
}

In the example above, Dog inherits from Animal. Dog is the subclass and Animal is the superclass. The property Name and the method Eat are both ‘inherited’ from Animal. This means that we can access the members defined in Animal on objects of type Dog.

Dog dog = new Dog() { Name = "Fido" };
dog.Eat();
Fido is eating.

Notice how we can call the Eat method on a Dog even though it was defined in Animal. This is inheritance of members at display.

Of course, we can also access the members that are only defined in Dog.

dog.Bark();
Fido is barking.

However, inheritance also implies inheriting type. Dog doesn’t just inherit Animal’s members, it also becomes a subtype of Animal. This enables subtype polymorphism. Dog can be treated as an Animal and used wherever an Animal is expected.

Animal animal = new Dog() { Name = "Rocky" };
animal.Eat();
Rocky is eating.

However, since the method Bark is only defined in Dog and not Animal we can only call the Bark method when our compile-time type is Dog.

animal.Bark();
(1,8): error CS1061: 'Animal' does not contain a definition for 'Bark' and no accessible extension method 'Bark' accepting a first argument of type 'Animal' could be found (are you missing a using directive or an assembly reference?)

Tip

Return to the chapter on run-time types vs compile-time types if the terminology is confusing.

Since Animal is a class we can also of course also instantiate that.

Animal animal = new Animal() { Name = "Rocky" };

Warning

Inheritance is often touted as a mechanism to model real-world relationships. However, viewing real-world relationships as strictly hierarchical, as inheritance suggests, can lead to misunderstandings and overly complicated designs. The world around us is full of complex, cross-cutting relationships that cannot be easily encapsulated within a hierarchy.

Consider the statement “a dog is an animal”. While this seems straightforward and easily modeled as a hierarchy (Dog : Animal), when we introduce additional facets of reality, like roles (e.g. a pet, a police dog, a stray dog), this hierarchical model quickly becomes convoluted. A dog can fulfill roles like a pet, a police dog, or a stray dog, at different times or even simultaneously. These roles do not fit neatly into an inheritance hierarchy.

In later chapters we will discuss the fragile base class problem and the maxim prefer composition over inheritance. For now, just remember that if all you want is subtype polymorphism, it’s better to use interfaces, which provide the contract for behavior without inheriting unwanted members or being tied to the parent’s evolution.

Note

While a class can implement multiple interfaces, it can only inherit from a single class.

See also

Some languages, like C++, but not C#, support ‘multiple inheritance’. Allowing subclasses to have multiple parents leads to a problem known as ‘The Diamond Problem’ which different languages have different ways of handling.

Key point

Inheritance allows a class to inherit the members and type of another class. Use inheritance only if you need both these things.