Introduction
In our previous parts of the learning OOP series, we were talking more of compile time polymorphism, the params keyword, Inheritance, the base keyword and so on. This part of the article series will focus more on runtime polymorphism also called late Binding. We'll use the same technique of learning, less theory and more hands-on. We'll use small code snippets to learn the concept more deeply. Mastering this concept is like learning more than 50% of OOP.
Pre-requisites
Since this is the third part of the series, I expect my readers to be experts in Compile-time polymorphism and inheritance. Although it doesn't matter if you start learning from this article, you can use the other articles later.
Roadmap
Our roadmap for learning OOP is clear, but let's just revise it.
Runtime Polymorphism or Late Binding or Dynamic Binding
In simple C# language, in runtime polymorphism or method overriding we can override a method in a base class by creating a similar method in the derived class; this can be done using the inheritance principle and using “virtual & override” keywords.
What are New and Override keywords in C#?
Create a console application named InheritanceAndPolymorphism in your Visual Studio.
Just add two classes and keep the Program.cs as it is.The two classes will be named ClassA.cs and ClassB.cs and add one method in each class as follows:
ClassA
public class ClassA
{
public void AAA()
{
Console.WriteLine("ClassA AAA");
}
public void BBB()
{
Console.WriteLine("ClassA BBB");
}
public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}
ClassB
public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
}
public void BBB()
{
Console.WriteLine("ClassB BBB");
}
public void CCC()
{
Console.WriteLine("ClassB CCC");
}
}
Program.cs
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
}
}
We see both classes, ClassA and ClassB, have the same number of methods with similar names in both the classes. Now let's inherit ClassA from ClassB and create instances of the classes and call their methods in program.cs.
So our code for the two classes become:
/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
}
public void BBB()
{
Console.WriteLine("ClassB BBB");
}
public void CCC()
{
Console.WriteLine("ClassB CCC");
}
}
/// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public void AAA()
{
Console.WriteLine("ClassA AAA");
}
public void BBB()
{
Console.WriteLine("ClassA BBB");
}
public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}
Program.cs
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA x = new ClassA();
ClassB y=new ClassB();
ClassB z=new ClassA();
x.AAA(); x.BBB(); x.CCC();
y.AAA(); y.BBB();y.CCC();
z.AAA(); z.BBB(); z.CCC();
}
}
Now press F5, in other words run the code, what do we get?
Output
ClassA AAA
ClassA BBB
ClassA CCC
ClassB AAA
ClassB BBB
ClassB CCC
ClassB AAA
ClassB BBB
ClassB CCC
But with the compiler output we also got three warnings.
Warnings
'InheritanceAndPolymorphism.ClassA.AAA()' hides inherited member 'InheritanceAndPolymorphism.ClassB.AAA()'. Use the new keyword if hiding was intended.
'InheritanceAndPolymorphism.ClassA.BBB()' hides inherited member 'InheritanceAndPolymorphism.ClassB.BBB()'. Use the new keyword if hiding was intended.
'InheritanceAndPolymorphism.ClassA.CCC()' hides inherited member 'InheritanceAndPolymorphism.ClassB.CCC()'. Use the new keyword if hiding was intended.
Point to remember: In C#, We can equate an object of a base class to a derived class but not vice versa.
Class ClassB is the super-class of class ClassA. That means ClassA is the derived class and ClassB is the base class. The class ClassA comprises ClassB and something more. So we can conclude that an object of ClassA is bigger than an object of ClassB. Since ClassA is inherited from ClassB, it contains its own methods and properties and moreover it will also contain methods/properties that are inherited from ClassB too.
Let's take the case of object y. It looks like ClassB and is initialized by creating an object that also looks like ClassB, well and good. Now, when we call the methods AAA and BBB and CCC through the object y we understand that it will call them from ClassB.
Object x looks like that of ClassA, in other words the derived class. It is initialized to an object that looks like ClassA. When we call AAA, the BBB and CCC methods through x, calls AAA, BBB and CCC from ClassA.
Now there is a somewhat tricky situation we are dealing with.
Object z again looks like ClassB, but it is now initialized to an object that looks like ClassA that does not provide an error as explained earlier. But there is no change at all in the output we get and the behavior is identical to that of object y. Therefore initializing it to an object that looks like ClassB or ClassA does not seem to matter.
Experiment
Let's experiment with the code and put an override behind AAA and new behind the BBB methods of ClassA, in other words the derived class.
Our code:
ClassB
/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
}
public void BBB()
{
Console.WriteLine("ClassB BBB");
}
public void CCC()
{
Console.WriteLine("ClassB CCC");
}
}
ClassA
/// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public override void AAA()
{
Console.WriteLine("ClassA AAA");
}
public new void BBB()
{
Console.WriteLine("ClassA BBB");
}
public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}
Program.cs
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB y = new ClassB();
ClassA x = new ClassA();
ClassB z = new ClassA();
y.AAA(); y.BBB(); y.CCC();
x.AAA(); x.BBB(); x.CCC();
z.AAA(); z.BBB(); z.CCC();
Console.ReadKey();
}
}
We get the output:
Error: 'InheritanceAndPolymorphism.ClassA.AAA()': cannot override inherited member 'InheritanceAndPolymorphism.ClassB.AAA()' because it is not marked virtual, abstract, or override
* InheritanceAndPolymorphism: It's the namespace I used for my console application, so you can ignore that.
We got the error after we added these two modifiers to the derived class methods. The error tells us to mark the methods virtual, abstract or override in the base class.
OK, how does it matter to me?
I marked all the methods of the base class as virtual.
Now our code and output looks like:
/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public virtual void AAA()
{
Console.WriteLine("ClassB AAA");
}
public virtual void BBB()
{
Console.WriteLine("ClassB BBB");
}
public virtual void CCC()
{
Console.WriteLine("ClassB CCC");
}
}
/// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public override void AAA()
{
Console.WriteLine("ClassA AAA");
}
public new void BBB()
{
Console.WriteLine("ClassA BBB");
}
public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB y = new ClassB();
ClassA x = new ClassA();
ClassB z = new ClassA();
y.AAA(); y.BBB(); y.CCC();
x.AAA(); x.BBB(); x.CCC();
z.AAA(); z.BBB(); z.CCC();
Console.ReadKey();
}
}
Output
ClassB AAA
ClassB BBB
ClassB CCC
ClassA AAA
ClassA BBB
ClassA CCC
ClassA AAA
ClassB BBB
ClassB CCC
Point to remember: The override modifier is necessary since the derived class methods will get first priority and be called upon.
We see here that there is only a single small change in the workings of the object z only and not in x and y. This strange output occurred only after we added the virtual modifier to the base class methods. The difference is in the object z; z looks like the base class ClassB but is initialized to an instance that looks like that of derived class ClassA. C# knows this fact. When we run z.AAA(), C# remembers that instance z was initialized by a ClassA object and hence it first goes to class ClassA, too obvious. Here the method has a modifier override that literally means, forget about the data type of z which is ClassB, call AAA from ClassA since it overrides the AAA of the base class. The override modifier is needed since the derived class methods will get first priority and be called upon.
We wanted to override the AAA of the base class ClassB. We are in fact telling C# that this AAA is similar to the AAA of the one in the base class.
The new keyword acts the exact opposite to the override keyword. The method BBB as we see has the new modifier. z.BBB() calls BBB from ClassB and not ClassA. New means that the method BBB is a new method and it has absolutely nothing to do with the BBB in the base class. It may have the same name, BBB, as in the base class, but that is only a coincidence. Since z looks like ClassB, the BBB of ClassB is called even though there is a BBB in ClassA. When we do not write any modifier, then it is assumed that we wrote new. So every time we write a method, C# assumes it has nothing to do with the base class.
Point to remember: These modifiers like new and override can only be used if the method in the base class is a virtual method. Virtual means that the base class is granting us permission to invoke the method from the derived class and not the base class. But, we need to add the modifier override if our derived class method must be called.
Runtime polymorphism with three classes
Let's get into some more action. Let's involve one more class in the play. Let's add a class named ClassC and design our three classes and program.cs as follows:
/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
}
public virtual void BBB()
{
Console.WriteLine("ClassB BBB");
}
public virtual void CCC()
{
Console.WriteLine("ClassB CCC");
}
}
/// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public virtual void AAA()
{
Console.WriteLine("ClassA AAA");
}
public new void BBB()
{
Console.WriteLine("ClassA BBB");
}
public override void CCC()
{
Console.WriteLine("ClassA CCC");
}
}
/// <summary>
/// Class C, acting as a derived class
/// </summary>
public class ClassC : ClassA
{
public override void AAA()
{
Console.WriteLine("ClassC AAA");
}
public void CCC()
{
Console.WriteLine("ClassC CCC");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB y = new ClassA();
ClassB x = new ClassC();
ClassA z = new ClassC();
y.AAA(); y.BBB(); y.CCC();
x.AAA(); x.BBB(); x.CCC();
z.AAA(); z.BBB(); z.CCC();
Console.ReadKey();
}
}
Output
ClassB AAA
ClassB BBB
ClassA CCC
ClassB AAA
ClassB BBB
ClassA CCC
ClassC AAA
ClassA BBB
ClassA CCC
Don't be scared of the long example that we have taken. This will help you to learn the concept in detail. We have already learned that we can initialize a base object to a derived object. But the reverse will result in an error. This leads to an instance of a base class being initialized to an instance of the derived class. So the question is now, which method will be called when. The method from the base class or from the derived class.
Point to remember: If the base class object declares the method virtual and the derived class uses the modifier override, the derived class method will be called. Otherwise the base class method will be executed. Therefore for virtual methods, the data type created is decided only at run time.
Point to remember: All the methods not marked with virtual are non virtual and the method to be called is decided at compile time, depending upon the static data type of the object.
If the object of a class is initialized to the same data type, none of the preceding rules would apply. Whenever we have a mismatch, we always need rules to resolve the mismatch. So the result could be a scenario where an object to a base class can call a method in the derived class.
The object y that looks like it is of ClassB but is initialized here to the derived class ClassA.
y.AAA(), first looks into the class ClassB. Here it verifies whether the method AAA is marked virtual. The answer is an emphatic no and hence everything comes to halt and the method AAA is called from the class ClassB.
y.BBB also does the same thing, but the method now is defined virtual in class ClassB. Thus C# looks at the class ClassB, the one it was initialized to. Here BBB is marked with the modifier “new”. That means BBB is a new method that has nothing to do with the one in the base class. They only accidentally share the same name. So since there is no method called BBB (as it is a new BBB) in the derived class, the one from the base class is called. In the scene of y.CCC(), the same above steps are followed again, but in the class ClassB, we see the modifier override that by behaviour overrides the method in the base class. We are actually telling C# to call this method in the class ClassA and not the one in the base class, ClassB.
I just got this picture from the internet that depicts our current situation of classes. We are learning the concept like charm now. OOP is becoming easy now.
The object x that also looks like that of class ClassB, is now initialized with an object that looks like our newly introduced class ClassC and not ClassA like before. Since AAA is a non-virtual method it is called from ClassB. In the case of method BBB, C# now looks into the class ClassC. Here it does not find a method named BBB and so ultimately propagates and now looks into class ClassA. Therefore the preceding rules repeat on and on and it is called from the class ClassB. In the case of x.CCC, in the class ClassC, it is already marked new by default and therefore this method has nothing to do with the one declared in the class ClassB. So the one from ClassC is not called but the one from class ClassB where it is marked as override.
Now if we modify our CCC method a bit in ClassC and change it to the code shown below:
public override void CCC()
{
Console.WriteLine("ClassC CCC");
}
We changed the default new to override, the CCC of ClassC will now be called.
The last object z looks like ClassA but is now initialized to an object that looks like the derived class ClassC, we know we can do this. So z.AAA() when called, looks first into class ClassA where it is flagged as virtual. Do you recall that AAA is non-virtual in class ClassB but marked as virtual in ClassA. From now on, the method AAA is virtual in ClassC also but not in class ClassB. Virtual always flows from upwards to downwards like a waterfall. Since AAA() is marked virtual, we now look into class ClassC. Here it is marked override and therefore AAA() is called from the class ClassC. In the case of BBB(), BBB() is marked virtual in the class ClassB and new in ClassA, but since there is no method BBB in ClassC, none of the modifier matters at all in this case. Finally it gets invoked from class ClassA. Finally for method CCC, in class ClassC it is marked as new. Hence it has no relation with the CCC in class ClassA that results in the method CCC getting invoked from ClassA but not ClassB.
The following is one more example.
internal class A
{
public virtual void X()
{
}
}
internal class B : A
{
public new void X()
{
}
}
internal class C : B
{
public override void X()
{
}
}
In the preceding example the code is very much self explanatory, the output that we'll get is:
Error: 'InheritanceAndPolymorphism.C.X()': cannot override inherited member 'InheritanceAndPolymorphism.B.X()' because it is not marked virtual, abstract, or override
Strange! We got an error since the method X() in class B is marked new. That means it hides the X() of class A. If we talk about class C, B does not supply a method named X. The method X defined in class B has nothing to do with the method X defined in class A. This means that the method X of class B does not inherit the virtual modifier from the method X() of class A. This is what the compiler complained about. Since the method X in B has no virtual modifier, in C we cannot use the modifier override. We can, however, use the modifier new and remove the warning.
Cut off relations
internal class A
{
public virtual void X()
{
Console.WriteLine("Class: A ; Method X");
}
}
internal class B : A
{
public new virtual void X()
{
Console.WriteLine("Class: B ; Method X");
}
}
internal class C : B
{
public override void X()
{
Console.WriteLine("Class: C ; Method X");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
A a = new C();
a.X();
B b = new C();
b.X();
Console.ReadKey();
}
}
Output
Class: A ; Method X
Class: C ; Method X
If in the code above we remove the modifier override from X() in class C, we get:
Output
Class: A ; Method X
Class: B ; Method X
Yes, that's the problem with virtual methods. Sometimes they are too confusing, the result is entirely different from what we expect. Object a looks like a A but is initialized to the derived class C. Since X is virtual, C# now looks into class C. But before looking into the class C, it realizes that in class B, X is new. That's it, this thing cuts of all connections with the X in A. Thus the keyword new is preceded with virtual, otherwise the override modifier would give us an error in class C. Since X in class B is marked as a new method, having nothing to do with the class A, class C inherits a new that also has nothing to do with the class A. The X in class C is related to the X of class B and not of class A. Thus the X of class A is invoked.
In the second case object b looks like class B now but in turn is initialized to an object of class C. C# first looks at class B. Here X is new and virtual both, that makes it a unique method X. Sadly, the X in C has the override modifier that sees to it that the X of C hides the X of B. This calls the X of C instead. If we remove the override modifier from X in class C then the default will be new, that cuts off the relation from the X of B. Thus, as it is, a new method, the X of B gets invoked.
A virtual method cannot be marked by the modifiers static, abstract or override. A non-virtual method is said to be invariant. This means that the same method is always called, irrespective of whether one exists in the base class or derived class. In a virtual method the run-time type of the object determines which method is to be called and not the compile-time type as is in the case of non-virtual methods. For a virtual method there exists a most derived implementation that is always called.
Runtime Polymorphism with four classes
OK! We did a lot of coding. How about if I tell you that we will add one more class to our code, yes that is class ClassD. So we dive deeper into the concepts of Polymorphism and Inheritance.
We add one more class to the three-class solution on which we were working on (remember?). So our new class is named ClassD.
Let's take our new class into action.
/// <summary>
/// Class A
/// </summary>
public class ClassA
{
public virtual void XXX()
{
Console.WriteLine("ClassA XXX");
}
}
/// <summary>
/// ClassB
/// </summary>
public class ClassB:ClassA
{
public override void XXX()
{
Console.WriteLine("ClassB XXX");
}
}
/// <summary>
/// Class C
/// </summary>
public class ClassC : ClassB
{
public virtual new void XXX()
{
Console.WriteLine("ClassC XXX");
}
}
/// <summary>
/// Class D
/// </summary>
public class ClassD : ClassC
{
public override void XXX()
{
Console.WriteLine("ClassD XXX");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA a = new ClassD();
ClassB b = new ClassD();
ClassC c=new ClassD();
ClassD d=new ClassD();
a.XXX();
b.XXX();
c.XXX();
d.XXX();
Console.ReadKey();
}
}
Output
ClassB XXX
ClassB XXX
ClassD XXX
ClassD XXX
Explanation
One last explanation of virtual and override will be a bit complex.
The first output, ClassB XXX, is the outcome of the statement a.XXX(). We have the method XXX marked virtual in class ClassA. Therefore, when using the new keyword, we now proceed to class ClassB and not ClassD as explained earlier. Here, XXX has an override and since C# knows that class ClassC inherits this function XXX. In the class ClassC, since it is marked as new, C# will now go back and does not proceed further to class ClassD. Finally the method XXX is called from class ClassB as shown in the output above.
If we change the method XXX in class ClassC to override, then C# will proceed to class ClassD and call the XXX of class ClassD since it overrides the XXX of ClassC.
/// <summary>
/// Class C
/// </summary>
public class ClassC : ClassB
{
public override void XXX()
{
Console.WriteLine("ClassC XXX");
}
}
Remove the override from XXX in class ClassD and the method will get invoked from class ClassC since the default is new.
When we talk about object b, everything seems similar to object a, since it overrides the XXX of class ClassA.
When we restore the defaults, let's have a look at the third line. Object c here looks like ClassC. In class ClassC, XXX() is a new and therefore it has no connection with the earlier XXX methods. In class ClassD, we actually override the XXX of class ClassC and so the XXX of ClassD is invoked. Just remove the override and then it will be invoked from class ClassC. The object d does not follow any of the preceding protocols since both sides of the equal sign are of the same data types.
Point to remember: An override method is a method that has the override modifier included on it. This introduces a new implementation of a method. We can't use the modifiers such as new, static or virtual along with override. But abstract is permitted.
The following is another example.
/// <summary>
/// Class A
/// </summary>
public class ClassA
{
public virtual void XXX()
{
Console.WriteLine("ClassA XXX");
}
}
/// <summary>
/// ClassB
/// </summary>
public class ClassB:ClassA
{
public override void XXX()
{
base.XXX();
Console.WriteLine("ClassB XXX");
}
}
/// <summary>
/// Class C
/// </summary>
public class ClassC : ClassB
{
public override void XXX()
{
base.XXX();
Console.WriteLine("ClassC XXX");
}
}
/// <summary>
/// Class D
/// </summary>
public class ClassD : ClassC
{
public override void XXX()
{
Console.WriteLine("ClassD XXX");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA a = new ClassB();
a.XXX();
ClassB b = new ClassC();
b.XXX();
Console.ReadKey();
}
}
Output
ClassA XXX
ClassB XXX
ClassA XXX
ClassB XXX
ClassC XXX
When we use the reserved keyword base, we can access the base class methods. Here whether or not XXX is virtual, it will be treated as non-virtual by the keyword base. Thus the base class XXX will always be called. The object a already knows that XXX is virtual. When it reaches ClassB, it sees base.XXX() and hence it calls the XXX method of ClassA. But in the second case, it first goes to class ClassC, where it calls the base.XXX, in other words the method XXX of class ClassB, that in turn invokes the method XXX of the class ClassA.
The infinite loop
/// <summary>
/// Class A
/// </summary>
public class ClassA
{
public virtual void XXX()
{
Console.WriteLine("ClassA XXX");
}
}
/// <summary>
/// ClassB
/// </summary>
public class ClassB:ClassA
{
public override void XXX()
{
((ClassA)this).XXX();
Console.WriteLine("ClassB XXX");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA a = new ClassB();
a.XXX();
}
}
Output
Error: {Cannot evaluate expression because the current thread is in a stack overflow state.}
In this kind of case no casting will stop the infinite loop. Therefore even though this is being cast to a class ClassA, it will always call XXX from class ClassB and not ClassA. So we get no output.
Summary
Let's summarize all the points to remember from this big article.
- In C#, We can equate an object of a base class to a derived class but not vice versa.
- The override modifier is necessary since the derived class methods will get first priority and be called upon.
- These modifiers like new and override can only be used if the method in the base class is a virtual method. Virtual means that the base class is granting us permission to invoke the method from the derived class and not the base class. But, we need to add the modifier override if our derived class method must be called.
- If the base class object declared the method virtual and the derived class used the modifier override, the derived class method will be called. Otherwise the base class method will be executed. Therefore for virtual methods, the data type created is decided at run time only.
- All the methods not marked with virtual are non-virtual and the method to be called is decided at compile time, depending upon the static data type of the object.
- An override method is a method that has the override modifier included on it. This introduces a new implementation of a method. We can't use the modifiers such as new, static or virtual along with override. But abstract is permitted.
Conclusion
This has been a long article and my readers have requested that I write short articles, but I can't help myself. I write in a flow until and unless I make the entire concept clear. I do not wish to stop. My purpose is to share each and every detail in an easy manner, so that you start loving OOP. However in this article I have tried to categorize the sections under various headings for the sake of readability.
In this article we learned the concept of runtime polymorphism and inheritance.We covered most of the scenarios by doing a hands-on lab. We'll be covering more topics in my future articles. Feel free to write comments or queries. Do like my article and rate it if it helped you by any means, that will make me happy. Happy coding.
Read more
For more technical articles you can reach out to CodeTeddy
My other series of articles,
Happy coding !