Inheritance Concepts in C#

( 6 users )

Inheritance in another object-oriented programming principle. Inheritance is a way of including features of a class into some other class. The class which wraps common features that can be re-used by other classes is called base class (or parent class). The classes which re-use these features are called derived classes (or child classes). This substantially allows us to reuse the code and design classes in a parent-child type of relationship.

Taking a real-world example, let's say we have a wristwatch. A wristwatch generally has a battery cell, a circuit board, gears, a control panel, three needles, and a user interface. Now, a wristwatch can be a type of royal wristwatch which is gold plated with advanced control panel and an excellent user interface. Also, a wristwatch can be a type of sports watch with a timer, an advanced control panel, and a sporty user interface.

C# Inheritance wrist watch example

Now, let's say we have a company that manufactures both of these types of wristwatches. Both of these types of wrist watches have many things in common. If the company starts building both of these types of wrist watches from scratch every time from different machines, it would cost a lot to the company because some of the components can be built from the same device (a class). Here is where the Inheritance has a role to play. We need only one machine (i.e., one Class) which can build standard components for both types of wristwatches and two other devices manufacturing a royal wristwatch and a sports wristwatch.

Keeping this real-world scenario in mind lets design our first program with Inheritance.

Designing Inheritance

First of all, we are going to write a bare minimum program that shows how to design Inheritance using classes. We will write a base class "WristWatch" which includes three methods "SetTime", "Start" and "GetFeatures".


using System;
 
class WristWatch
{
	public string name;
	public int seconds;
	public int minutes;
	public int hours;
	public string circuit;
	public string[] gears;
	public string controlPanel;
	
	public WristWatch() {}
	
	public WristWatch(string name, string circuit, string[] gears, string controlPanel)
	{
		this.name = name;
		
		this.seconds = 0;
		this.minutes = 0;
		this.hours = 0;
		
		this.circuit = circuit;
		this.gears = gears;
		this.controlPanel = controlPanel;
	}
	
	public void SetTime(int hours, int minutes, int seconds)
	{
		this.hours = hours;
		this.minutes = minutes;
		this.seconds = seconds;
	}
	
	public void Start()
	{
		Console.WriteLine($"Started watch {this.name} at {this.hours}:{this.minutes}:{this.seconds}");
	}
	
	public void GetFeatures()
	{
		Console.WriteLine("This watch includes the following features");
		Console.WriteLine($"Circuit : {this.circuit}");
		Console.WriteLine($"Gears : {String.Join(", ", this.gears)}");
		Console.WriteLine($"Control Panel : {this.controlPanel}");
	}
}
		

The general syntax of inheriting a base class is : class DerivedClass : BaseClass { ... }

Now, we will write another class "RoyalWristWatch" which will inherit our base class "WristWatch".


class RoyalWristWatch : WristWatch
{
	string plating;
	public RoyalWristWatch(string name, string circuit, string[] gears, string controlPanel, string plating)
	{
		this.name = name;
		
		this.seconds = 0;
		this.minutes = 0;
		this.hours = 0;
		
		this.circuit = circuit;
		this.gears = gears;
		this.controlPanel = controlPanel;
		
		this.plating = plating;
	}
	
	public void GetFeatures()
	{
		Console.WriteLine("This watch includes the following features");
		Console.WriteLine($"Circuit : {this.circuit}");
		Console.WriteLine($"Gears : {String.Join(", ", this.gears)}");
		Console.WriteLine($"Control Panel : {this.controlPanel}");
		Console.WriteLine($"Plating : {this.plating}");
	}
}
		

If you see the above derived class "RoyalWristWatch", we have not included the data members or variables like seconds, minutes, controlPanel etc. Still we are using them inside the method GetFeatures(). This is because due to inheritance the data members of the base class are available inside the derived class. In the derived class, we have only included one data member which is plating.

Now, we will write our main method where we will see in action how inheritance works.

Run this code


class Program
{
	static void Main(string[] args)
	{
		Console.WriteLine("Inheritance");
		Console.WriteLine("");
		
		Console.WriteLine("Creating Base wrist watch");
		var wristWatch = new WristWatch("Base Model", "Base Circuit", new string[] {"BG1", "BG2"}, "Base control panel");
		wristWatch.GetFeatures();
		wristWatch.SetTime(1, 0, 0);
		wristWatch.Start();
		
		Console.WriteLine();
		
		Console.WriteLine("Creating Royal wrist watch");
		var royalWristWatch = new RoyalWristWatch("Royal Model", "Royal Circuit", new string[] {"RG1", "RG2", "RG3"}, "Royal control panel", "Gold");
		royalWristWatch.GetFeatures();
		royalWristWatch.SetTime(2, 10, 0);
		royalWristWatch.Start();
		
		Console.WriteLine();
		
		Console.Write("Press any key to continue...");
		Console.ReadKey(true);
	}
}
		

If you see the above program, we have created two objects wristWatch and royalWristWatch. On the royalWristWatch object, we have called the method SetTime and Start even though the class "RoyalWristWatch" doesn't include these two methods. However, due to inheritance, the methods "SetTime" and "Start" are inherited from the parent class "WristWatch" and hence we are able to re-use them.

Note : You may encounter the following warning :

inheritance_protected.cs(70,14): warning CS0108: 'RoyalWristWatch.GetFeatures()' hides inherited member 'WristWatch.GetFeatures()'. Use the new keyword if hiding was intended.

Don't worry about it at this point in time. This is fine for now as this is not an error and we will discuss about this later in next chapter.

The "protected" access modifier

Everything seems fine in the above program in terms of what we expect from inheritance however there is one problem. If we see in both the classes, we have public data members. These data members should not be exposed outside the class because anyone can modify them and change the behavior of the class. Also, we can not make them "private" because then the derived class would not be able to use them. The "public" and "private" access modified we have already discussed in previous chapters. Now, we are going to learn how the "protected" access modifier works and used in inheritance.

The "protected" access modifier restricts the data member and methods of a class to access outside the class, however it allows the derived class to access the protected base class members. So, let's modify our previous example and change the data members from public to protected.

Run this code


class WristWatch
{
	protected string name;
	protected int seconds;
	protected int minutes;
	protected int hours;
	protected string circuit;
	protected string[] gears;
	protected string controlPanel;
	
	public WristWatch(string name, string circuit, string[] gears, string controlPanel)
	{
		this.name = name;
		
		this.seconds = 0;
		this.minutes = 0;
		this.hours = 0;
		
		this.circuit = circuit;
		this.gears = gears;
		this.controlPanel = controlPanel;
	}
	
	public void SetTime(int hours, int minutes, int seconds)
	{
		this.hours = hours;
		this.minutes = minutes;
		this.seconds = seconds;
	}
	
	public void Start()
	{
		Console.WriteLine($"Started watch {this.name} at {this.hours}:{this.minutes}:{this.seconds}");
	}
	
	public void GetFeatures()
	{
		Console.WriteLine("This watch includes the following features");
		Console.WriteLine($"Circuit : {this.circuit}");
		Console.WriteLine($"Gears : {String.Join(", ", this.gears)}");
		Console.WriteLine($"Control Panel : {this.controlPanel}");
	}
}
		

Now that we have changed our class "WristWatch", if we execute the program, it will be executed with no issues. However, if we try to access any data member say "name" through the wristWatch object, then the compiler will show the following error.

inheritance_protected.cs(89,32): error CS0122: 'WristWatch.name' is inaccessible due to its protection level

This means protected members can only be accessed through inheritance.

Constructors with Inheritance

Let's have a re-look at the two constructors - base class' constructors and derived class' constructors.



// Base class' constructor

public WristWatch(string name, string circuit, string[] gears, string controlPanel)
{
	this.name = name;
	
	this.seconds = 0;
	this.minutes = 0;
	this.hours = 0;
	
	this.circuit = circuit;
	this.gears = gears;
	this.controlPanel = controlPanel;
}
		


// Derived class' constructor

public RoyalWristWatch(string name, string circuit, string[] gears, string controlPanel, string plating)
{
	this.name = name;
	
	this.seconds = 0;
	this.minutes = 0;
	this.hours = 0;
	
	this.circuit = circuit;
	this.gears = gears;
	this.controlPanel = controlPanel;
	
	this.plating = plating;
}
		

If you see here, the same code is repeated. We are initializing our data members in the same way in both base class and derived class. This code is defeating our purpose of re-usability. Also, in case some variables are not accessible to the derived class (which the base class requires to initialize) then we need to have some provision. Fortunately, C# provides us a way which is we can call the base class constructor from the derived type.

The general syntax of calling a base class' constructor from derived class is:

derived-constructor (argument-list) : base (argument-list) { ... }

Here base is the keyword that represents the instance of base class' constructor. Let's modify the constructor of our derived class "RoyalWristWatch".


public RoyalWristWatch(string name, string circuit, string[] gears, string controlPanel, string plating)
	: base (name, circuit, gears, controlPanel)
{	
	this.seconds = 0;
	this.minutes = 0;
	this.hours = 0;
	
	this.plating = plating;
}
		

Since now we are calling the base class' constructor, the base class will initialize the variables - "seconds", "minutes" and "hours". So, we can remove that too from our derived class' constructor.

Run this code


public RoyalWristWatch(string name, string circuit, string[] gears, string controlPanel, string plating)
	: base (name, circuit, gears, controlPanel)
{
	this.plating = plating;
}
		

Now, if we try to execute our program, it will be executed without any issues. One thing to note here is since the keyword "base" is an instance of base class' constructor, we can call base class' methods like "base.Meth1()" as well or use any data members which are accessible.

Derived objects with base class' references

Let's assume we have another class "SportWristWatch" derived from base class "WristWatch" similar to "RoyalWristWatch". So, let's create objects of both these classes.


RoyalWristWatch royalWristWatch = new RoyalWristWatch();

SportWristWatch sportWristWatch = new SportWristWatch();
		

Now, it's not possible to assign an object of RoyalWristWatch to SportWristWatch reference and vice versa. It doesn't even make any sense to write such code. This means the following lines will give you a compile time error.


RoyalWristWatch royalWristWatch1 = sportWristWatch; // Compile time error
SportWristWatch sportWristWatch1 = royalWristWatch; // Compile time error
		

However, if we had taken the base reference, then such assignments are possible. Let's see the code below.


WristWatch wristWatch1 = royalWristWatch; // Correct
WristWatch wristWatch2 = sportWristWatch; // Correct
		

C# provides us such flexibility to assign derived class objects to base class references. This concept is crucial when we do not know the type of object, but we know that the object is a derived object. So, even without prior knowledge of the object, we could still use the object and invoke methods on it. All these references are resolved at runtime. Let's see some line of codes to understand what it means. Consider the below method.


ShowWristWatchFeatures(WristWatch wristWatch)
{
	wristWatch.GetFeatures();
}
		

In the above code, we can pass any type of watch in the method argument because of Inheritance. Hence, calling this method with three different types of WristWatch is absolutely fine.


ShowWristWatchFeatures(wristWatch);
ShowWristWatchFeatures(royalWristWatch);
ShowWristWatchFeatures(sportWristWatch);
		

Now, you must have realized the real power and beauty of Inheritance.

Here is the complete code. This is important to understand before moving further. In case of any doubts, you can ask your question(s) in the comments section.

Run this code


using System;
 
class WristWatch
{
	protected string name;
	protected int seconds;
	protected int minutes;
	protected int hours;
	protected string circuit;
	protected string[] gears;
	protected string controlPanel;
	
	public WristWatch(string name, string circuit, string[] gears, string controlPanel)
	{
		this.name = name;
		
		this.seconds = 0;
		this.minutes = 0;
		this.hours = 0;
		
		this.circuit = circuit;
		this.gears = gears;
		this.controlPanel = controlPanel;
	}
	
	public void SetTime(int hours, int minutes, int seconds)
	{
		this.hours = hours;
		this.minutes = minutes;
		this.seconds = seconds;
	}
	
	public void Start()
	{
		Console.WriteLine($"Started watch {this.name} at {this.hours}:{this.minutes}:{this.seconds}");
	}
	
	public void GetFeatures()
	{
		Console.WriteLine("This watch includes the following features");
		Console.WriteLine($"Circuit : {this.circuit}");
		Console.WriteLine($"Gears : {String.Join(", ", this.gears)}");
		Console.WriteLine($"Control Panel : {this.controlPanel}");
	}
}
 
class RoyalWristWatch : WristWatch
{
	string plating;
	public RoyalWristWatch(string name, string circuit, string[] gears, string controlPanel, string plating)
		: base (name, circuit, gears, controlPanel)
	{
		this.plating = plating;
	}
	
	public void GetFeatures()
	{
		Console.WriteLine("This watch includes the following features");
		Console.WriteLine($"Circuit : {this.circuit}");
		Console.WriteLine($"Gears : {String.Join(", ", this.gears)}");
		Console.WriteLine($"Control Panel : {this.controlPanel}");
		Console.WriteLine($"Plating : {this.plating}");
	}
}

class SportWristWatch : WristWatch
{
	string timerType;
	public SportWristWatch(string name, string circuit, string[] gears, string controlPanel, string timerType)
		: base (name, circuit, gears, controlPanel)
	{
		this.timerType = timerType;
	}
	
	public void GetFeatures()
	{
		Console.WriteLine("This watch includes the following features");
		Console.WriteLine($"Circuit : {this.circuit}");
		Console.WriteLine($"Gears : {String.Join(", ", this.gears)}");
		Console.WriteLine($"Control Panel : {this.controlPanel}");
		Console.WriteLine($"TimerType : {this.timerType}");
	}
}

class Program
{
	static void Main(string[] args)
	{
		Console.WriteLine("Inheritance");
		Console.WriteLine("");
		
		Console.WriteLine("Creating Base wrist watch");
		WristWatch wristWatch = new WristWatch("Base Model", "Base Circuit", new string[] {"BG1", "BG2"}, "Base control panel");
		wristWatch.SetTime(1, 0, 0);
		wristWatch.Start();
		
		Console.WriteLine();
		
		Console.WriteLine("Creating Royal wrist watch");
		WristWatch royalWristWatch = new RoyalWristWatch("Royal Model", "Royal Circuit", new string[] {"RG1", "RG2", "RG3"}, "Royal control panel", "Gold");
		royalWristWatch.SetTime(2, 10, 0);
		royalWristWatch.Start();
		
		Console.WriteLine();
		
		Console.WriteLine("Creating Sport wrist watch");
		SportWristWatch sportWristWatch = new SportWristWatch("Sport Model", "Water Proof Circuit", new string[] {"RG1", "RG2", "RG3"}, "Sporty control panel", "Advanced");
		sportWristWatch.SetTime(1, 15, 0);
		sportWristWatch.Start();
		
		Console.WriteLine();
		
		Console.WriteLine("----- Showing features : Base wrist watch -----");
		ShowWristWatchFeatures(wristWatch);
		Console.WriteLine("\n ----- Showing features : Royal wrist watch -----");
		ShowWristWatchFeatures(royalWristWatch);
		Console.WriteLine("\n ----- Showing features : Sport wrist watch -----");
		ShowWristWatchFeatures(sportWristWatch);
		
		Console.WriteLine();
		
		Console.Write("Press any key to continue...");
		Console.ReadKey(true);
	}
	
	static void ShowWristWatchFeatures(WristWatch wristWatch)
	{
		wristWatch.GetFeatures();
	}
}
		

Did you note the output of this program? See this.


 ----- Showing features : Royal wrist watch -----
This watch includes the following features
Circuit : Royal Circuit
Gears : RG1, RG2, RG3
Control Panel : Royal control panel

 ----- Showing features : Sport wrist watch -----
This watch includes the following features
Circuit : Break Free Circuit
Gears : SG1, SG2, SG3
Control Panel : Sporty control panel
		

If we closely see the above output, we must note that output section "Showing Features" of RoyalWristWatch is missing the "Plating" information which should show "Gold" and for the SportWristWatch the "TimerType" is missing which should show "Advanced". Why is it so? The answer to this will be found in the first topic in next chapter which is "Overriding".

To Do

* Note : These actions will be locked once done and hence can not be reverted.

1. Track your progress [Earn 200 points]

2. Provide your ratings to this chapter [Earn 100 points]

0
Object Oriented Programming Principles and Overloading
Overriding and Abstract Classes
Note : At the end of this chapter, there is a ToDo section where you have to mark this chapter as completed to record your progress.