Inheritance in C#

In object-oriented programming, inheritance is another type of relationship between classes. Inheritance is a mechanism of reusing the functionalities of one class into another related class.

Inheritance is referred to as "is a" relationship. In the real world example, a customer is a person. In the same way, a student is a person and an employee is also a person. They all have some common things, for example, they all have a first name, middle name, and last name. So to translate this into object-oriented programming, we can create the Person class with first name, middle name, and last name properties and inherit the Customer, Student, and Employee classes from the Person class. That way we don't need to create the same properties in all classes and avoid the violation of the DRY (Do not Repeat Yourself) principle.

Note that the inheritance can only be used with related classes where they should have some common behaviors and perfectly substitutable. Follow the Liskov Substitution Principle in inheritance.

Inheritance

In C#, use the : symbol to inherit a class from another class. For example, the following Employee class inherits from the Person class in C#.

Example: Class Inheritance in C#
class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string GetFullName(){ 
        return FirstName + " " + LastName;
    }
}

class Employee : Person
{
    public int EmployeeId { get; set; }
    public string CompanyName { get; set; }
    
}

In the above example, the Person class is called the base class or the parent class, and the Employee class is called the derived class or the child class.

The Employee class inherits from the Person class and so it automatically acquires all the public members of the Person class. It means even if the Employee class does not include FirstName, LastName properties and GetFullName() method, an object of the Employee class will have all the properties and methods of the Person class along with its own members.

Example: Inherited Members
Employee emp = new Employee();
emp.FirstName = "Steve";
emp.LastName = "Jobs";
emp.EmployeeId = 1;
emp.CompanyName = "Apple";

var fullname = emp.GetFullName(); //Steve Jobs 

Note that C# does not allow a class to inherit multiple classes. A class can only achieve multiple inheritances through interfaces.

Role of Access Modifiers in Inheritance

Access modifiers play an important role in inheritance. Access modifiers of each member in the base class impact their accessibility in the derived class.

Public Members

The public members of the base class are accessible in the derived class and also become part of the derived class object.

Example: Inheritance of Public Members

class Person
{
    public string FirstName { get; set; } // can be inherited
}

class Employee : Person
{
        
}

Employee emp = new Employee();
emp.FirstName = "Bill"; // valid

Private Members

The private members of the base class cannot be accessed directly from the derived class and cannot be part of the derived class object.

Example: Inheritance of Private Members
class Person
{
    private string FirstName { get; set; }  // cannot be inherited
}

class Employee : Person
{
   
}

Employee emp = new Employee();
emp.FirstName; // Compile-time error

Protected Members

The protected members of the base class can be accessible in the derived class but cannot be a part of the derived class object.

Example: Inheritance of Protected Members
class Person
{
    protected string FirstName { get; set; }
}

class Employee : Person
{
    public int GetName()
    {
        return this.FirstName;// valid
    }
}

Employee emp = new Employee();
emp.GetName();// valid
emp.FirstName; // Compile-time error. 

Internal Members

Internal members are accessible in the derived class and are part of the derived class object.

Example: Inheritance of Internal Members
class Person
{
    internal string FirstName { get; set; } 
}

class Employee : Person
{
        
}

Employee emp = new Employee();
emp.Name= "Steve";// valid

Constructors

Creating an object of the derived class will first call the constructor of the base class and then the derived class. If there are multiple levels of inheritance then the constructor of the first base class will be called and then the second base class and so on.

Example: Constructors in Inheritance

class Person
{
    public Person()
    {
	    Console.WriteLine("Person Constructor");
	}
}

class Employee : Person
{
    public Employee()
    {
	    Console.WriteLine("Employee Constructor");
	}   
}

Employee emp = new Employee();
Output:
Person Constructor
Employee Constructor

Use the base keyword in the derived class to access the public members of the base class. For example, the following calls the base class's parameterized constructor using the :base().

Example: base Keyword
class Person
{
    public Person()
    {
	    Console.WriteLine("Person Constructor");
	}

    public Person(string val)
    {
	    Console.WriteLine(val);
	}
}

class Employee : Person
{
    public Employee() : base("Parameterized constructor of base class")
    {
	    Console.WriteLine("Employee Constructor");
	}   
}

Employee emp = new Employee();
Output:
Parameterized constructor of base class
Employee Constructor

Object Initialization

You can create an instance of the derived class and assign it to a variable of the base class or derived class. The instance's properties and methods are depending on the type of variable it is assigned to. Here, a type can be a class or an interface, or an abstract class.

The following table list supported members based on a variable type and instance type.

Instance variable Instance Type Instance Members of
Base type Base type Base type
Base type Derived type Base type
Derived type Derived type Base and derived type

The following program demonstrates supported members based on the variable type:

Example: Object Creation
class Person
{
    public int Id { get; set; }  
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
}

class Employee : Person
{
    public string CompanyName { get; set; }
    public decimal Salary { get; set; }
}

public class Program
{
	public static void Main()
	{
        Person per1 = new Person();
        per1.Id = 1; //valid
        per1.FirstName = "James"; //valid
        per1.LastName = "Bond"; //valid

        //per1.CompanyName; // not supported
        //per1.Salary;  // not supported

        Person per2 = new Employee();
        per2.Id = 2; //valid
        per2.FirstName = "Bill"; //valid
        per2.LastName = "Gates"; //valid

        //per2.CompanyName; // not supported
        //per2.Salary;  // not supported
 
        Employee emp = new Employee();
        emp.Id = 1; //valid
        emp.FirstName = "Steve";//valid
        emp.LastName = "Jobs";//valid
        emp.CompanyName = "XYZ"; //valid
        emp.Salary = 10000; //valid 

        //invalid, can't assign base type to derived type
        //Employee emp = new Person();
     }
}

In the above example, the type of per2 is Person, so it will only expose public properties of the Person type even if an object type is the Employee. However, the type of emp is Employee and so it exposes all the public properties of both classes. Note that the base type object cannot be assigned to the derived type variable.

Type Conversion

The base type converts to the base class implicitly whereas the derived type must be converted to the base class explicitly using the as operator.

Example: Type Conversion From Base to Derived

public static void Display(Employee emp){
    Console.WriteLine($"Name: {emp.FirstName} {emp.LastName}");
}

public static void Main()
{
    Person per = new Employee();
    per.FirstName="Steve";
    per.LastName="Jobs";

    //Cannot convert from Person to Employee implicitly
    Display(per);//error 

    Display(per as Employee);//valid, explicit conversion

    Employee emp = new Employee();
    emp.FirstName = "Abdul";
    emp.LastName = "Kalam";
    Display(emp);//valid
}    
Output:
Employee Name: Steve Jobs
Company Name:
Employee Name: Abdul Kalam
Company Name:

Types of Inheritance

There are different types of inheritance supported in C# based on how the classes are inherited.

Single Inheritance

In a single inheritance, only one derived class inherits a single base class.

Single Inheritance

Multi-level Inheritance

In multi-level inheritance, a derived class inherits from a base class and then the same derived class becomes a base class for another derived class. Practically, there are no limits on the level of inheritance, but you should avoid it.

Multi-level Inheritance

Hierarchical Inheritance

In hierarchical inheritance, multiple derived classes inherit from a single base class.

Hierarchical Inheritance

Hybrid Inheritance

Hybrid inheritance is a combination of multi-level and hierarchical inheritance.

Multiple Inheritance

Multiple Inheritance

In multiple inheritance, a class inherits from multiple interfaces. Note that C# does not support deriving multiple base classes. Use interfaces for multiple inheritance.

Hybrid Inheritance

Important Points:

  • In C#, three types can participate in inheritance: Class, Struct, and Interface.
  • A class can inherit a single class only. It cannot inherit from multiple classes.
  • A class cannot inherit from a struct.
  • A class can inherit (implement) one or more interfaces.
  • A Struct can inherit from one or more interfaces. However, it cannot inherit from another struct or class.
  • An interface can inherit from one or more interfaces but cannot inherit from a class or a struct.
  • Constructors or destructors cannot be inherited.