Overriding and Abstract Classes

( 1 user )

In the chapter Object oriented programming and Overloading, we read about the two types of Polymorphism and we did cover the first type "Overloading" which is compile time polymorphism. The another type being "Overriding" which is a run time polymorphism. So, let's cover this in detail.

Note : It's important to note that this chapter is strictly in continuation to previous chapter Inheritance Concepts . So, you need to cover the previous chapter as a prerequisite to continue this chapter.

Overriding

Overriding, as the name says itself, is a way to override or hide a base class member in the derived class.

Note: We are continuing with the example from previous chapter.

In the example from last chapter, where we assigned the derived class' object to base class reference, we saw that even though the object was of type "RoyalWristWatch" the runtime was executing the base class method "GetFeatures()" rather than the child class. This is because we have not mentioned that in the case of derived class the runtime needs to call the derived class method not the base class even when the reference is taken from the base class.

To achieve overriding, we specify a keyword "virtual" in the base class' method and the keyword "override" in the derived class' method . So, let's first modify our base class "WristWatch" furthermore.


public virtual 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 "virtual" keyword lets the derived class know that this base class' method can be hidden/overridden. Now since this method is virtual, the derived class could override this method in its class body. So, let's modify the derived classes "RoyalWristWatch" and "SportWristWatch".

RoyalWristWatch


public override 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}");
}
		

SportWristWatch


public override 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}");
}
		

The main program [no changes here]


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();
	}
}
		

Now if we execute our program with these modifications, it will give us the desired results as follows.


Creating Base wrist watch
Started watch Base Model at 1:0:0

Creating Royal wrist watch
Started watch Royal Model at 2:10:0

Creating Sport wrist watch
Started watch Sport Model at 1:15:0

----- Showing features : Base wrist watch -----
This watch includes the following features
Circuit : Base Circuit
Gears : BG1, BG2
Control Panel : Base control panel

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

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

As you can see, with overriding, the above output is showing "Plating" and "TimerType" in case of "RoyalWristWatch" and "SportWristWatch" respectively. Also, note that the compiler warning (mentioned in previous chapter) is too gone now. High Five!

Till now we have seen how Inheritance works, its use cases and how overriding helps us to achieve dynamic method invocation.

Multi level Inheritance

A derived class can also act as base class and hence can be derived in another class. This is called multi-level Inheritance. Let's add one more class in our program say "SwimmingSportWristWatch". This class will be derived from the class "SportWristWatch".

C# Multi level inheritance wrist watch example

Run this code


class SwimmingSportWristWatch : SportWristWatch
{
	int waterProtectionLevel;
	public SwimmingSportWristWatch(string name, string circuit, string[] gears, string controlPanel, string timerType, int waterProtectionLevel)
		: base (name, circuit, gears, controlPanel, timerType)
	{
		this.waterProtectionLevel = waterProtectionLevel;
	}
	
	public override 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}");
		Console.WriteLine($"WaterProtectionLevel : {this.waterProtectionLevel}");
	}
}
		

Now, let's modify our main program to use this type of wristwatch and include the below lines.


Console.WriteLine("Creating Swimming Sport wrist watch");
SwimmingSportWristWatch swimmingSportWristWatch = 
	new SwimmingSportWristWatch("Swimming Sport Model", "Water Proof and Break Free Circuit", new string[] {"WSG1", "WSG2", "WSG3"}, "Sporty control panel", "Advanced", 5);
swimmingSportWristWatch.SetTime(1, 15, 0);
swimmingSportWristWatch.Start();
		

Also, include the below line to call the method "GetFeatures()" on this object.


Console.WriteLine("\n ----- Showing features : Swimming sport wrist watch -----");
ShowWristWatchFeatures(swimmingSportWristWatch);
		

This is how we can create a multi-level hierarchy. In this case the derivation of the class "SwimmingSportWristWatch" goes like this "WristWatch" -> "SportWristWatch" -> "SwimmingSportWristWatch" which makes it a three level Inheritance. Similarly, we can create more levels as and when required.

Abstract classes

In the above examples, our base class "WristWatch" can have instances which we can create using "new WristWatch(...)". There are some situations where we do not want our base class to have instances. Rather than the only thing we want it to be extendible means, it can only be inherited. You might be thinking - "What would be that scenario?". Considering the same example, suppose a company does not want to create a basic model of "WristWatch" and they are only interested in selling "RoyalWristWatch", "SportWristWatch" and "SwimmingSportWristWatch". In this case, we need to restrict our base class not to have any kind of instances and the sole purpose would be providing the features to the derived classes. Such classes are called Abstract classes.

An abstract class can not have an instance. However, it is allowed for Inheritance or extension. The general format of declaring a class as abstract is:

abstract class class-name { ... }

So, now when the company has stopped creating a base model which "WristWatch", it doesn't make any sense to keep the "GetFeatures()" method inside the class "WristWatch". This is because the child classes are overriding it with their own definition. However, we can not simply remove this method otherwise it will defeat the purpose of assigning child objects to base class references. Hence, we could never use our "ShowWristWatchFeatures(WristWatch wristWatch)" method and call the "GetFeatures()" method on any child instances passed to it.

Well, fortunately, there is a solution. C# allows us to create a method with empty definition and we call such methods as abstract methods. An abstract method is virtual by default so there is no need to provide a "virtual" keyword to it. The general syntax of an abstract method is:

abstract type method-name(parameter-list);

In case of abstract method, we need not to provide any definition, only declaration is sufficient. This allows child classes to override it and at the same time allow child class objects assign to base class references. Let's modify our examples now to make the "WristWatch" an abstract class with and abstract method "GetFeatures()".


abstract 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 abstract void GetFeatures();
}
		

Also, we need to remove the base class instance creation from our "Main" method which leaves us with the following "Main" method body.

Run this code


static void Main(string[] args)
{
	Console.WriteLine("Inheritance");
	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", "Break Free Circuit", new string[] {"SG1", "SG2", "SG3"}, "Sporty control panel", "Advanced");
	sportWristWatch.SetTime(1, 15, 0);
	sportWristWatch.Start();
	
	Console.WriteLine();
	
	Console.WriteLine("Creating Swimming Sport wrist watch");
	SwimmingSportWristWatch swimmingSportWristWatch = 
		new SwimmingSportWristWatch("Swimming Sport Model", "Water Proof and Break Free Circuit", new string[] {"WSG1", "WSG2", "WSG3"}, "Sporty control panel", "Advanced", 5);
	swimmingSportWristWatch.SetTime(1, 20, 15);
	swimmingSportWristWatch.Start();
	
	Console.WriteLine();
	
	Console.WriteLine("\n ----- Showing features : Royal wrist watch -----");
	ShowWristWatchFeatures(royalWristWatch);
	Console.WriteLine("\n ----- Showing features : Sport wrist watch -----");
	ShowWristWatchFeatures(sportWristWatch);
	Console.WriteLine("\n ----- Showing features : Swimming sport wrist watch -----");
	ShowWristWatchFeatures(swimmingSportWristWatch);
	
	Console.WriteLine();
	
	Console.Write("Press any key to continue...");
	Console.ReadKey(true);
}
		

Now, if we try to execute the program, it will execute without any issues.

Before concluding this chapter, there is one more keyword which is important to take about and that is "sealed". This is used in context class inheritance. When a class is declared as "sealed", it can not be inherited. It will be a compile time error if we try to inherit from a sealed class.

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
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.