Diving Into OOP (Day 4) : Polymorphism and Inheritance (All About Abstract Classes in C#)

1. Introduction

We have learned a lot about polymorphism and Inheritance. In this article of the series “Diving Into OOP”, we'll discuss the hottest and most exciting topic of OOP in C#, Abstract Classes. The concept of Abstract classes is the same for any other language, but in C# we deal with it in a bit of a different way. Abstract classes play a different and very interesting role in polymorphism and inheritance. We'll cover all the aspects of abstract classes with our hands-on lab and theory as an explanation to what output we get. We'll also list the points to remember at the end of the article.

Abstract classes
Pre-requisites

I wonder, we are dealing with the fourth part of our learning objective. Now my only expectation with my readers is to enjoy the series.

2. Roadmap

Let's recall our road map.

Roadmap 
 
 
3. Abstract Classes

The following definition is from the MSDN:

"The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class. An abstract class cannot be instantiated. The purpose of an abstract class is to provide a common definition of a base class that multiple derived classes can share. For example, a class library may define an abstract class that is used as a parameter to many of its functions, and require programmers using that library to provide their own implementation of the class by creating a derived class.

Abstract classes may also define abstract methods. This is accomplished by adding the keyword abstract before the return type of the method.”

4. Abstract classes in action

Add a console application named “InheritanceAndPolymorphism” in your Visual Studio. You'll get a class named Program, just add one more class named ClassA. Note that ClassA should be marked abstract and add the following code to ClassA and the Program class:

  1. using System;  
  2.   
  3. namespace InheritanceAndPolymorphism  
  4. {  
  5.     public abstract class ClassA  
  6.     {  
  7.     }   
  8.      /// <summary>  
  9.      /// Program: used to execute the method.  
  10.     /// Contains Main method.  
  11.     /// </summary>  
  12.    public class Program  
  13.    {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.            ClassA classA = new ClassA();  
  17.            Console.ReadKey();  
  18.         }  
  19.     }   

Compile the code.

Output

Compile time error: Cannot create an instance of the abstract class or interface 'InheritanceAndPolymorphism.ClassA'

Point to remember: We cannot create an object of abstract class using the new keyword.

Now we go into understanding the concept. No power can stop abstract keyword to be written before a class. It acts as a modifier to the class. We cannot create an object of abstract class using the new keyword. It seems that the class is useless for us since we cannot use it for other practical purposes as we previously did.

5. Non abstract method definition in abstract class

Let's add some code to our abstract class as in the following:

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10. }  
  11. /// <summary>  
  12. /// Program: used to execute the method.  
  13. /// Contains Main method.  
  14. /// </summary>  
  15. public class Program  
  16. {  
  17.     private static void Main(string[] args)  
  18.     {  
  19.        ClassA classA = new ClassA();  
  20.        Console.ReadKey();  
  21.     }  

We again see the error that we encountered earlier. Again it reminds us that we cannot use new if we have already used an abstract modifier.

6. Abstract class acting as a base class

Let's add one more class as in the following:

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4.     public abstract class ClassA  
  5.     {  
  6.         public int a;  
  7.         public void XXX()  
  8.     {  
  9.   }  
  10. }  
  11. /// <summary>  
  12. /// Derived class.  
  13. /// Class derived from abstract class ClassA  
  14. /// </summary>  
  15. public class ClassB:ClassA  
  16. {  
  17. }  
  18. /// <summary>  
  19. /// Program: used to execute the method.  
  20. /// Contains Main method.  
  21. /// </summary>  
  22. public class Program  
  23. {  
  24.     private static void Main(string[] args)  
  25.     {  
  26.        ClassB classB = new ClassB();  
  27.        Console.ReadKey();  
  28.     }  

We get no error. A class can be derived from an abstract class. Creating an object of ClassB does not give us an error.

Point to remember: A class can be derived from an abstract class.

Point to remember: A class derived from an abstract class can create an object.

7. Non abstract method declaration in abstract class

Another scenario:

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.   
  10.     }   
  11.     public void YYY(); 
    }  
  12.   
  13. /// <summary>  
  14. /// Derived class.  
  15. /// Class derived from abstract class ClassA.  
  16. /// </summary>  
  17. public class ClassB:ClassA  
  18. {   
  19. }  
  20.   
  21. /// <summary>  
  22. /// Program: used to execute the method.  
  23. /// Contains Main method.  
  24. /// </summary>  
  25. public class Program  
  26. {  
  27.     private static void Main(string[] args)  
  28.     {  
  29.         ClassB classB = new ClassB();  
  30.         Console.ReadKey();  
  31.     }  

We just declared a method named YYY() in our abstract class ClassA.

Compile the code; we get:

Output

Compile time error: 'InheritanceAndPolymorphism.ClassA.YYY()' must declare a body because it is not marked abstract, extern, or partial

InheritanceAndPolymorphism is the namespace I used for my console application so you can ignore that, no need to confuse it with the logic.

In the code above, we just added a method declaration in the abstract class. An abstract method indicates that the actual definition or code of the method is created somewhere else. The method prototype declared in abstract class must also be declared abstract as per the rules of C#.

8. Abstract method declaration in abstract class

Just make the method YYY() as abstract in ClassA as in the following:

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10.     abstract public void YYY();  
  11. }  
  12. /// <summary>  
  13. /// Derived class.  
  14. /// Class derived from abstract class ClassA.  
  15. /// </summary>  
  16. public class ClassB:ClassA  
  17. {  
  18. }  
  19. /// <summary>  
  20. /// Program: used to execute the method.  
  21. /// Contains Main method.  
  22. /// </summary>  
  23. public class Program  
  24. {  
  25.     private static void Main(string[] args)  
  26.     {  
  27.        ClassB classB = new ClassB();  
  28.        Console.ReadKey();  
  29.     }  

Output

Compiler error: 'InheritanceAndPolymorphism.ClassB' does not implement inherited abstract member 'InheritanceAndPolymorphism.ClassA.YYY()'

Point to remember: If we declare any method as abstract in our abstract class, then it's the responsibility of the derived class to provide the body of that abstract method, unless a body is provided for that abstract method, we cannot create an object of that derived class.

In the previously specified scenario, we declared method YYY() as abstract in ClassA. Since ClassB derives from ClassA, now it becomes the responsibility of ClassB to provide the body of that abstract method, else we cannot create an object of ClassB.

9. Abstract method implementation in derived class

Now provide a body of method YYY() in ClassB, let's see what happens,

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4.     public abstract class ClassA  
  5.     {  
  6.         public int a;  
  7.         public void XXX()  
  8.     {  
  9. }  
  10.   
  11. abstract public void YYY();  
  12. }  
  13. /// <summary>  
  14. /// Derived class.  
  15. /// Class derived from abstract class ClassA.  
  16. /// </summary>  
  17. public class ClassB:ClassA  
  18. {  
  19.     public void YYY()  
  20.     {  
  21.     }  
  22. }  
  23. /// <summary>  
  24. /// Program: used to execute the method.  
  25. /// Contains Main method.  
  26. /// </summary>  
  27. public class Program  
  28. {  
  29.     private static void Main(string[] args)  
  30.     {  
  31.        ClassB classB = new ClassB();  
  32.        Console.ReadKey();  
  33.     }  

Every thing seems fine now, but now compile the code.

Output

There are two compile-time errors this time.

Compile time error:
'InheritanceAndPolymorphism.ClassB' does not implement inherited abstract member 'InheritanceAndPolymorphism.ClassA.YYY()'

Compile time warning: 'InheritanceAndPolymorphism.ClassB.YYY()' hides inherited member 'InheritanceAndPolymorphism.ClassA.YYY()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Compile time warning

We have been continuously trying to compile our code, but with no success until now. The compiler error indicates clearly that both of our base and derived classes contain the same method named YYY().

If both our derived class and base class contains the method with the same name, an error will always occur. The only way to overcome this error is to derive a class explicitly and add the modifier override to its method signature. We have already discussed such scenarios in our previous parts of the articles of Diving Into OOP series.

Let's add the override keyword before the derived class method YYY().
  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10.     abstract public void YYY();  
  11. }  
  12. /// <summary>  
  13. /// Derived class.  
  14. /// Class derived from abstract class ClassA.  
  15. /// </summary>  
  16. public class ClassB:ClassA  
  17. {  
  18.     public override void YYY()  
  19.     {  
  20.     }  
  21. }  
  22. /// <summary>  
  23. /// Program: used to execute the method.  
  24. /// Contains Main method.  
  25. /// </summary>  
  26. public class Program  
  27. {  
  28.     private static void Main(string[] args)  
  29.     {  
  30.         ClassB classB = new ClassB();  
  31.         Console.ReadKey();  
  32.     }  

Enjoy 

We get no warning or error now.

10. Abstract method implementation in derived class with different return type

Let's just change the return type of the method YYY() in the derived class as in the following:
  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10.     abstract public void YYY();  
  11. }  
  12. /// <summary>  
  13. /// Derived class.  
  14. /// Class derived from abstract class ClassA.  
  15. /// </summary>  
  16. public class ClassB:ClassA  
  17. {  
  18.     public override int YYY()  
  19.     {  
  20.     }  
  21. }  
  22.   
  23. /// <summary>  
  24. /// Program: used to execute the method.  
  25. /// Contains Main method.  
  26. /// </summary>  
  27. public class Program  
  28. {  
  29.     private static void Main(string[] args)  
  30.     {  
  31.         ClassB classB = new ClassB();  
  32.         Console.ReadKey();  
  33.     }  

We changed the return type of method YYY from void to int in the derived class. Compile the code.

Output

Compile time error: 'InheritanceAndPolymorphism.ClassB.YYY()': return type must be 'void' to match overridden member 'InheritanceAndPolymorphism.ClassA.YYY()'

Therefore there is one more constraint.

Point to remember: When we override an abstract method from a derived class, we cannot change the parameters ed to it or the return type irrespective of the number of methods declared as abstract in the abstract class.

Let's see the implementation of the second line mentioned in “points to remember”:

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10.     abstract public void YYY();  
  11.     abstract public void YYY1();  
  12.     abstract public void YYY2();  
  13.     abstract public void YYY3();  
  14. }  
  15.   
  16. /// <summary>  
  17. /// Derived class.  
  18. /// Class derived from abstract class ClassA.  
  19. /// </summary>  
  20. public class ClassB:ClassA  
  21. {  
  22.     public override void YYY()  
  23.     {  
  24.     }  
  25. }  
  26. /// <summary>  
  27. /// Program: used to execute the method.  
  28. /// Contains Main method.  
  29. /// </summary>  
  30. public class Program  
  31. {  
  32.    private static void Main(string[] args)  
  33.    {  
  34.         ClassB classB = new ClassB();  
  35.         Console.ReadKey();  
  36.    }  

Compiler errors

'InheritanceAndPolymorphism.ClassB' does not implement inherited abstract member 'InheritanceAndPolymorphism.ClassA.YYY3()'

'InheritanceAndPolymorphism.ClassB' does not implement inherited abstract member 'InheritanceAndPolymorphism.ClassA.YYY2()'

'InheritanceAndPolymorphism.ClassB' does not implement inherited abstract member 'InheritanceAndPolymorphism.ClassA.YYY1()'

If we implement these three methods in a derived class then we'll get no error.

Point to remember: An abstract class means that the class is incomplete and cannot be directly used. An abstract class can only be used as a base class for other classes to derive from.

11. Variable initialization in abstract class

Therefore as seen earlier, we get an error if we use a new keyword on an abstract class. If we do not Initialize a variable in an abstract class like we used a, it will automatically have a default value of 0; that is what the compiler kept warning us about. We can initialize an int variable a of ClassA to any value we wish. The variables in an abstract class act similar to that in any other normal class.

12. Power of abstract class

Power of abstract class

Whenever a class remains incomplete, in other words we do not have the code for some methods, we mark those methods abstract and the class is marked abstract as well. And so we can compile our class without any error or blocker. Any other class can then derive from our abstract class but they need to implement the abstract, in other words our incomplete methods from the abstract class.

Abstract therefore enables us to write code for a part of the class and allows the others (derived classes) to complete the rest of the code.

13. Abstract method in non abstract class

Let's take another code block.

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10.     abstract public void YYY();  
  11. }  
  12. /// <summary>  
  13. /// Derived class.  
  14. /// Class derived from abstract class ClassA.  
  15. /// </summary>  
  16. public class ClassB:ClassA  
  17. {  
  18.     public override void YYY()  
  19.     {
        }         

  20. /// <summary>  
  21. /// Program: used to execute the method.  
  22. /// Contains Main method.  
  23. /// </summary>  
  24. public class Program  
  25. {  
  26.     private static void Main(string[] args)  
  27.     {  
  28.         ClassB classB = new ClassB();  
  29.         Console.ReadKey();  
  30.     }  
  31.  } 

Compile the code.

Output

Compiler error: 'InheritanceAndPolymorphism.ClassA.YYY()' is abstract but it is contained in non-abstract class 'InheritanceAndPolymorphism.ClassA'

We just removed the abstract keyword from class ClassA. The error clearly conveys a message that if a single method is marked abstract in a class then the class will need to be abstract as well.

Point to remember: If a class has even a single abstract method then the class must be declared abstract as well.

Point to remember: An abstract method also cannot use the modifiers such as static or virtual.

We can only have the abstract method in an abstract class. Any class that derives from an abstract class must provide an implementation to its abstract method. By default the modifier new is added to th derived class method, that makes it a new/different method.

14. Abstract base method

  1. /// <summary>  
  2. /// Abstract class ClassA  
  3. /// </summary>  
  4. public abstract class ClassA  
  5. {  
  6.     public int a;  
  7.     public void XXX()  
  8.     {  
  9.     }  
  10.     abstract public void YYY();  
  11. }  
  12. /// <summary>  
  13. /// Derived class.  
  14. /// Class derived from abstract class ClassA.  
  15. /// </summary>  
  16. public class ClassB:ClassA  
  17. {  
  18.     public override void YYY()  
  19.     {  
  20.         base.YYY();  
  21.     }  
  22. }  
  23.   
  24. /// <summary>  
  25. /// Program: used to execute the method.  
  26. /// Contains Main method.  
  27. /// </summary>  
  28. public class Program  
  29. {  
  30.     private static void Main(string[] args)  
  31.     {  
  32.         ClassB classB = new ClassB();  
  33.         Console.ReadKey();  
  34.     }  

Output

Compile time error: Cannot call an abstract base member: 'InheritanceAndPolymorphism.ClassA.YYY()'

We cannot call the method YYY() from the base class ClassA since it does not carry any implementation/code along with it and has also been declared abstract. Common sense prevails and C# of course does not allow us to call a method that does not contain code.

15. Abstract class acting as derived as well as base class

Let's modify our code a bit, and prepare our class structure something as follows:

  1. /// <summary>  
  2. /// Base class ClassA  
  3. /// </summary>  
  4. public class ClassA  
  5. {  
  6.     public virtual void XXX()  
  7.     {  
  8.         Console.WriteLine("ClassA XXX");  
  9.     }  
  10. }  
  11. /// <summary>  
  12. /// Derived abstract class.  
  13. /// Class derived from base class ClassA.  
  14. /// </summary>  
  15. public abstract class ClassB:ClassA  
  16. {  
  17.     public new abstract void XXX();  
  18. }  
  19. public class ClassC:ClassB  
  20. {  
  21.     public override void XXX()  
  22.     {  
  23.        System.Console.WriteLine("ClassC XXX");  
  24.     }  
  25. }  
  26. /// <summary>  
  27. /// Program: used to execute the method.  
  28. /// Contains Main method.  
  29. /// </summary>  
  30. public class Program  
  31. {  
  32.     private static void Main(string[] args)  
  33.     {  
  34.         ClassA classA = new ClassC();  
  35.         ClassB classB = new ClassC();  
  36.         classA.XXX(); classB.XXX();  
  37.     }  

Compile the code, and run.

Output

ClassA XXX
ClassC XXX

We created a base class named ClassA that is not abstract and added a virtual method XXX to it. Since the method is non-abstract but marked virtual, it must be overridden in its deriving class. We added one more class named ClassB and marked that class abstract, note that this class is derived from ClassA. So this class has a choice to override the method marked as virtual in the base class.But we'll do something different and tricky,

We marked the XXX method in this derived class as new abstract and did not provide any body to this method. Now what? We will add one more class ClassC, that will derive from ClassB. Class C has no choice but to override the method XXX. Therefore we override the method XXX in Class C.

In the main method we created two objects of ClassA, classA = new ClassC() and ClassB, classB = new ClassC().

The first object looks like that of ClassC but refers to ClassA and the second one again seems to be like ClassC but refers to ClassB.

In the case of classA.XXX() will definitely first look into the class ClassA. Here it finds the method XXX marked virtual. These kinds of scenarios we have already taken n number of times in our earlier articles where we discussed runtime polymorphism . C# will then crawl over to class ClassB. Here it gets shocked that the method XXX() is abstract, in other words there is no code or implementation for method XXX() and also that it is a method marked as new, thus severing all links with the base class. And so flow halts and all and the method XXX() from ClassA is executed.

In the case of b.XXX()(), since the method is new, the links to the base class becomes broken, we are left with no choice but to invoke the method from ClassC as it says override.

We cannot replace the modifier new with the keyword override for the method XXX() in the abstract class ClassB.

Let's replace the override modifier in ClassC with “new” as in the following:

  1. public class ClassC:ClassB  
  2. {  
  3.     public new void XXX()  
  4.     {  
  5.        System.Console.WriteLine("ClassC XXX");  
  6.     }  

Output

Compile time error: 'InheritanceAndPolymorphism.ClassC' does not implement inherited abstract member 'InheritanceAndPolymorphism.ClassB.XXX()'

The error indicates that there is no code for the method XXX. Remember the XXX() of class ClassA has nothing to do at all with that of ClassB and ClassC.

Also there is one more point to remember.

Point to remember: Virtual methods run slower that non virtual methods.

16. Can an abstract class be sealed?

Let's take this final question into consideration, let's test this too with an example as in the following:

  1. /// <summary>  
  2. /// sealed abstract class ClassA  
  3. /// </summary>  
  4. public sealed abstract class ClassA  
  5. {  
  6.     public abstract void XXX()  
  7.     {  
  8.        Console.WriteLine("ClassA XXX");  
  9.     }  
  10. }  
  11.   
  12. /// <summary>  
  13. /// Program: used to execute the method.  
  14. /// Contains Main method.  
  15. /// </summary>  
  16. public class Program  
  17. {  
  18.     private static void Main(string[] args)  
  19.     {  
  20.     }  

Compile the code.

Output

Compile time error: 'InheritanceAndPolymorphism.ClassA': an abstract class cannot be sealed or static

And so we get two points to remember.

Point to remember: Abstract class cannot be sealed class.

Point to remember: Abstract class cannot be a static class.

17. Points to remember

Let's sum up all the points to remember.

remember

  1. We cannot create an object of an abstract class using the new keyword.
  2. A class can be derived from an abstract class.
  3. A class derived from an abstract class can create an object.
  4. If we declare any method as abstract in our abstract class, then it's the responsibility of the derived class to provide the body of that abstract method, unless a body is provided for that abstract method, we cannot create an object of that derived class.
  5. When we override an abstract method from a derived class, we cannot change the parameters ed to it or the return type irrespective of the number of methods declared as abstract in an abstract class.
  6. An abstract class means that the class is incomplete and cannot be directly used. An abstract class can only be used as a base class for other classes to derive from.
  7. If a class has even a single abstract method, then the class must be declared abstract as well.
  8. An abstract method also cannot use the modifiers such as static or virtual.
  9. Virtual methods run slower that non-virtual methods.
  10. An abstract class cannot be a sealed class.
  11. An abstract class cannot be a static class.

18. Conclusion

With this article we have completed our understanding of inheritance and polymorphism. We have covered nearly all the aspects of polymorphism and inheritance. Abstract classes are one of my favorites so I just wanted to take them separately.I hope my readers enjoyed this article too and have learned about abstract classes in C#.

In my future articles of the series we'll be discussing other OOP features in C# with full hands-on labs and a lot of discussion.

Conclusion

Keep coding and enjoy reading

Also do not forget to rate/comment/like my article if it helped you by any means, this helps me to get motivated and encourages me to write more and more.

Next Recommended Readings