DEV Community

Cover image for C# OOP: Inheritance & Polymorphism
Adam Romig πŸ‡΅πŸ‡­
Adam Romig πŸ‡΅πŸ‡­

Posted on • Updated on • Originally published at romig.dev

C# OOP: Inheritance & Polymorphism

Jump to:

Let's learn how to make more complex classes and the objects we can make from them.

Inheritance

In object-oriented programming, we can create objects from classes. Though what if we want to make a class that's everything that another class is (properties and methods) but more? Should we copy & paste everything from one class to another? If that thought makes you cringe a bit, no worries. We can use inheritance for doing just this.

Inheritance allows a class to take on (inherit) properties & methods from another class. The class that is used as a basis for the new class is called the base class and the class that is inheriting from it is called the derived class. The derived class not only will contain the properties of the base class, but also properties of its own. And there can be multiple derived classes from a base class. In addition, a derived class can be a base class for another class.

The base/derived relationship can be understood as a "is a" clause.

  • An apple is a fruit.
  • A square is a shape. Also a triangle is a shape.
  • A pug is a dog, and a dog is an animal.

In C#, we designate that we are inheriting from another class using a colon : followed by the base class name. Then we can reference the base properties & methods.

class Animal
{
  public int Legs { get; set; }
  public bool hasFur { get; set; }

  public Animal()
  {
    // Animal constructor
  }
}

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

  public Dog(string name) : base()
  {
    // Dog constructor
    Legs = 4;
    hasFur = true;
    Name = name;
  }

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

public static void Main()
{
  Dog doggo = new Dog("Doggo");
  Console.WriteLine("{0} has {1} legs.", doggo.Name, doggo.Legs);
  // Doggo has 4 legs.
  doggo.Bark();
  // Woof!
}
Enter fullscreen mode Exit fullscreen mode

β†’ dotnetfiddle

Notice that both classes have their own constructors. Both constructors are called when the object is created, as well as destructors (if defined). The order for when they are called is outlined below:

  1. base constructor
  2. derived constructor
  3. derived destructor
  4. base destructor

Basically the call order is going inward (from base to derived) when constructing and outward when destructing. If there are multiple levels of derived classes (e.g. base > derived from base > derived from 1st derived), this pattern follows.

base()

Notice the base keyword in the Dog class's constructor. When used with a derived class constructor, it specifies which base class constructor that the derived class's constructor should be derived from. We need to specify which one because a class can have multiple constructors: a default (no parameters), with parameters, and others with different parameters (overloaded constructors).

The base class that is accessed is the base class specified in the class declaration, regardless if that class itself is based on another class.

The base keyword can also be used to access members of the base class from within the derived class. If the Animal class above had a method of its own, we could call it from within a method in the Dog class. i.e. base.MethodName();


Polymorphism

Dictionary Definition: the quality or state of existing in or assuming different forms

Polymorphism is declaring one interface but its implementation details are left to the derived classes.

Simply put, a member of a class can be implemented in different ways in different derived classes..

keywords when defining polymorphic methods

  • virtual - used on the base class member
  • override - used on the derived class member

Let's create a simple hero for a role-playing game. This Hero can specialize in a particular weapon discipline. We'll make a base class of a generic hero and each specialization is derived from it.

// Base Class
class Hero {
  public string Weapon { get; set; }
  public string Name { get; set; }

  public Hero(string name) {
    Name = name;
  Weapon = "pair of fists";
  }

  public virtual void Attack() {
  Console.WriteLine("Punch Punch!");
  }
}

// Derived Classes
class Rogue : Hero {
  public Rogue(string name) : base(name) {
    Name = name;
    Weapon = "dagger";
  }

  public override void Attack() {
    Console.WriteLine("Stab Stab!");
  }
}

class Archer : Hero {
  public Archer(string name) : base(name) {
    Name = name;
    Weapon = "bow & arrows";
  }

  public override void Attack() {
    Console.WriteLine("Pew Pew!");
  }
}

class Warrior : Hero {
  public Warrior(string name) : base(name) {
    Name = name;
    Weapon = "sword";
  }

  public override void Attack() {
    Console.WriteLine("Slash Slash!");
  }
}

public static void Main() {
  Hero avatar = new Rogue("Bob");
  Console.WriteLine("{0}'s weapon is a {1}.", avatar.Name, avatar.Weapon);
  // Bob's weapon is a dagger.
  avatar.Attack();
  // Stab Stab!
}
Enter fullscreen mode Exit fullscreen mode

β†’ dotnetfiddle

The use of polymorphism may not seem apparent right away but without it, the base class would have to account for each and every version of our hero types. This can lead to overly complicated spaghetti code and overloaded methods (methods with mutiple implementations, explained in detail later), all within the base class. These variations belong in the derived classes, keeping it organized. The purpose of polymorphism has a lot to do with having concise and semantic code that will be maintainable.

Using this example, the different Weapon attributes cannot be placed in the base Hero class. Not all heroes know how to use all weapons, only those who trained with them - hence the derived classes for each specialty.

In the .NET Fiddle link above, in the Main() method, go ahead and change the class that we're using to create our avatar (e.g. change Rogue to Archer, Warrior, or even just plain Hero).

Abstract Classes

Polymorphism can be taken a step further and the base class can be considered abstract, where there is no implementation at all. A regular base class can have polymorphic properties & methods and have its own that aren't defined by derived classes.

In an abstract class, the implementation must be defined in the derived classes.

Abstract classes:

  • cannot be instantiated; they must be derived from
  • may include concrete & abstract members
  • must have their implementations defined in the derived classes for all inherited abstract members

Why do this? Because in some situations, there is no meaningful need for the virtual member to have a separate definition in the base class. The class is declared as abstract to provide a sort of template.

abstract class Shape {
  public abstract void Draw();
}

class Circle : Shape {
  public override void Draw() {
    // draw a circle!
    Console.WriteLine("Imagine a circle is drawn");
  }
}

class Rectangle : Shape {
  public override void Draw() {
    // draw a rectangle!
    Console.WriteLine("Imagine a rectangle is drawn");
  }
}

public static void Main() {
  Shape c = new Circle();
  c.Draw();
  // Imagine a circle is drawn
}
Enter fullscreen mode Exit fullscreen mode

β†’ dotnetfiddle

Interfaces

An interface is a completely abstract class, containing only abstract properties and methods (no fields/variables). It's common to preface an interface's name with a capital letter I.

An interface simply describes what a class should do. The class implementing the interface must define how to accomplish the behaviors. Think of an interface as a strict guideline to creating the blueprint (class).

A simplified metaphor could be: A house is going to need walls and a roof but how they are built (quantities, sizes, colors, etc) is up to the architect.

Each member in the interface must be implemented in the derived class.

public interface IShape {
  void Draw();
  double Area();
}

class Circle : IShape {
  public double Radius { get; set; }

  public Circle(double radius) {
    Radius = radius;
  }

  public void Draw() {
    // draw a circle!
  }
  public double Area() {
    return Math.PI * Math.Pow(Radius, 2);
  }
}

public static void Main() {
  IShape c = new Circle(5);
  Console.WriteLine(c.Area());
  // 78.5398163397448
}
Enter fullscreen mode Exit fullscreen mode

β†’ dotnetfiddle

A class can inherit from just one base class but it can implement from multiple interfaces. So by using interfaces you can include behavior form multiple sources in a single class.

Maybe we want to make an object that implements from both the IShape and IAnimal interfaces: class AnimalShape : IShape, IAnimal { }


That escalated quickly!

To summarize, we've gone over how to create classes based on other classes through inheritance & understanding the basics of polymorphism and abstract classes including a short introduction to interfaces.

Main take-aways:

  • Think of the base/derived relationship as a "is a" clause.
  • We use a colon : to designate that a class is inheriting from a base class.
  • Polymorphism is having class members that can be defined in different ways when they are implemented in a derived class.
  • Abstract classes and interfaces can be used to provide a stricter guideline on how to build classes that implement them.

I admit that all of this information can be a lot to take in but hopefully you can imagine how these complex classes can give a program more structure and maintainability.

In my next article in this series, we'll learn about method overloading as a type of polymorphism.

Top comments (0)