Class Vs Abstract Class Vs Interfaces

In previous discussions, we have already seen Interfaces and Abstract Classes in action. Now, let us see live implementation of all three around the same use case. In the end, we will also see the differences between the same. And, then you choose, which is best fit for you.

Let us first discuss simple plain class. Below, I have written simple class to calculate area and perimeter.
  1. using System;  
  2. namespace Interface_Demo.Lib  
  3. {  
  4.     public class BaseClass  
  5.     {  
  6.         public int Sides  
  7.         {  
  8.             get;  
  9.             set;  
  10.         }  
  11.         public int Length  
  12.         {  
  13.             get;  
  14.             set;  
  15.         }  
  16.         //To fill in these props  
  17.         public BaseClass(int sides, int length)  
  18.             {  
  19.                 Sides = sides;  
  20.                 Length = length;  
  21.             }  
  22.             //Calculate Perimeter. Number of sides * Length of each side  
  23.         public int CalcPerimeter()  
  24.             {  
  25.                 return Sides * Length;  
  26.             }  
  27.             //Area differs for different object. Hence, it is kept virtual method to get overriden  
  28.             //in derived class  
  29.         public virtual int CalcArea()  
  30.         {  
  31.             throw new NotImplementedException();  
  32.         }  
  33.     }  
  34. }  
In this example, I have kept concrete implementation of perimeter calculation but area calculation is virtual method as different objects will have different area calculation, hence it needs to be implemented exclusively in derived class. Now, let us look at the derived class for the same.
  1. using System;  
  2. namespace Interface_Demo.Lib  
  3. {  
  4.     public class Square: BaseClass  
  5.     {  
  6.         public Square(int sides, int length): base(sides, length)  
  7.         {}  
  8.         public override int CalcArea()  
  9.         {  
  10.             return Sides * Length;  
  11.         }  
  12.     }  
  13. }  
Here, virtual method got implemented its way. Now, let's look at main program for the same.
  1. using Interface_Demo.Lib;  
  2. namespace Interface_Demo.Console  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.             {  
  8.                 var square = new Square(4, 4);  
  9.                 DisplayDimensions("Square", square);  
  10.                 System.Console.ReadLine();  
  11.             }  
  12.             //dynamic key will resolve an object at runtime. very useful keyword  
  13.         private static void DisplayDimensions(string objectType, dynamic square)  
  14.         {  
  15.             System.Console.WriteLine("Displaying Dimensions of " + objectType);  
  16.             System.Console.WriteLine("Number of Sides:- " + square.Sides);  
  17.             System.Console.WriteLine("Length of Each Side:- " + square.Length);  
  18.             System.Console.WriteLine("Area of " + objectType + " is " + square.CalcArea());  
  19.             System.Console.WriteLine("Perimeter of " + objectType + " is " + square.CalcPerimeter());  
  20.             System.Console.WriteLine("==============================================================");  
  21.         }  
  22.     }  
  23. }  
Now, when I run the above program, it will produce the following output.

This was just the case with simple plain class. Now, let us look at the same example with abstract class implementation. The following is the abstract class for the same.
  1. namespace Interface_Demo.Lib  
  2. {  
  3.     public abstract class AbstractBaseClass  
  4.     {  
  5.         public int Sides  
  6.         {  
  7.             get;  
  8.             set;  
  9.         }  
  10.         public int Length  
  11.         {  
  12.             get;  
  13.             set;  
  14.         }  
  15.         //To fill in these props  
  16.         public AbstractBaseClass(int sides, int length)  
  17.             {  
  18.                 Sides = sides;  
  19.                 Length = length;  
  20.             }  
  21.             //Calculate Perimeter. Number of sides * Length of each side  
  22.         public int CalcPerimeter()  
  23.         {  
  24.             return Sides * Length;  
  25.         }  
  26.         public abstract int CalcArea();  
  27.     }  
  28. }  
Here, in abstract class instead of keeping Virtual method, I just kept abstract method and abstract method cannot have body in base class. Now, let us see it's implementation in derived class.
  1. namespace Interface_Demo.Lib  
  2. {  
  3.     public class SquareAbstract: AbstractBaseClass  
  4.     {  
  5.         public SquareAbstract(int sides, int length): base(sides, length)  
  6.         {}  
  7.         public override int CalcArea()  
  8.         {  
  9.             return Sides * Length;  
  10.         }  
  11.     }  
  12. }  
Fairly simple and clean. Similarly, main program for the same, now looks like the following,
  1. using Interface_Demo.Lib;  
  2. namespace Interface_Demo.Console  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.             {  
  8.                 var square = new Square(4, 4);  
  9.                 DisplayDimensions("Square", square);  
  10.                 var squareAbstract = new SquareAbstract(4, 4);  
  11.                 DisplayDimensions("Square with abstract Implementation", squareAbstract);  
  12.                 System.Console.ReadLine();  
  13.             }  
  14.             //dynamic key will resolve an object at runtime. very useful keyword  
  15.         private static void DisplayDimensions(string objectType, dynamic square)  
  16.         {  
  17.             System.Console.WriteLine("Displaying Dimensions of " + objectType);  
  18.             System.Console.WriteLine("Number of Sides:- " + square.Sides);  
  19.             System.Console.WriteLine("Length of Each Side:- " + square.Length);  
  20.             System.Console.WriteLine("Area of " + objectType + " is " + square.CalcArea());  
  21.             System.Console.WriteLine("Perimeter of " + objectType + " is " + square.CalcPerimeter());  
  22.             System.Console.WriteLine("==============================================================");  
  23.         }  
  24.     }  
  25. }  
With the above change in place, when I run the same, it will produce the following output,



Similarly, we can implement the same stuff using interface. The following is the sample interface for the same and it's implementation.
  1. namespace Interface_Demo.Lib  
  2. {  
  3.     public interface ICircle  
  4.     {  
  5.         int radius  
  6.         {  
  7.             get;  
  8.             set;  
  9.         }  
  10.         //Only declarations, no Implementation  
  11.         double CalcPerimeter();  
  12.         double CalcArea();  
  13.     }  
  14. }  
  15. namespace Interface_Demo.Lib  
  16. {  
  17.     public interface ICircle  
  18.     {  
  19.         int radius  
  20.         {  
  21.             get;  
  22.             set;  
  23.         }  
  24.         //Only declarations, no Implementation  
  25.         double CalcPerimeter();  
  26.         double CalcArea();  
  27.     }  
  28. }  
  1. namespace Interface_Demo.Lib  
  2. {  
  3.     public class CircleInterface: ICircle  
  4.     {  
  5.         public int radius  
  6.         {  
  7.             get;  
  8.             set;  
  9.         }  
  10.         int ICircle.radius  
  11.         {  
  12.             get  
  13.             {  
  14.                 return radius;  
  15.             }  
  16.             set  
  17.             {  
  18.                 radius = value;  
  19.             }  
  20.         }  
  21.         public CircleInterface(int rad)  
  22.         {  
  23.             radius = rad;  
  24.         }  
  25.         public double CalcPerimeter()  
  26.         {  
  27.             return (2 * 3.14 * radius);  
  28.         }  
  29.         public double CalcArea()  
  30.         {  
  31.             return (3.14 * radius * radius);  
  32.         }  
  33.     }  
  34. }  
Now, the main program for the same will look like the following,
  1. using Interface_Demo.Lib;  
  2. namespace Interface_Demo.Console  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             var square = new Square(4, 4);  
  9.             DisplayDimensions("Square", square);  
  10.             var squareAbstract = new SquareAbstract(4, 4);  
  11.             DisplayDimensions("Square with abstract Implementation", squareAbstract);  
  12.             var circleInterface = new CircleInterface(5);  
  13.             DisplayCircle("Circle with Interface Implementation", circleInterface);  
  14.             System.Console.ReadLine();  
  15.         }  
  16.         private static void DisplayCircle(string objectType, CircleInterface circleInterface)  
  17.             {  
  18.                 System.Console.WriteLine("Displaying Dimensions of " + objectType);  
  19.                 System.Console.WriteLine("Circle with Radius:- " + circleInterface.radius);  
  20.                 System.Console.WriteLine("Area of " + objectType + " is " + circleInterface.CalcArea());  
  21.                 System.Console.WriteLine("Perimeter of " + objectType + " is " + circleInterface.CalcPerimeter());  
  22.                 System.Console.WriteLine("==============================================================");  
  23.             }  
  24.             //dynamic key will resolve an object at runtime. very useful keyword  
  25.         private static void DisplayDimensions(string objectType, dynamic square)  
  26.         {  
  27.             System.Console.WriteLine("Displaying Dimensions of " + objectType);  
  28.             System.Console.WriteLine("Number of Sides:- " + square.Sides);  
  29.             System.Console.WriteLine("Length of Each Side:- " + square.Length);  
  30.             System.Console.WriteLine("Area of " + objectType + " is " + square.CalcArea());  
  31.             System.Console.WriteLine("Perimeter of " + objectType + " is " + square.CalcPerimeter());  
  32.             System.Console.WriteLine("==============================================================");  
  33.         }  
  34.     }  
  35. }  
With the above change in place, it will produce the following output,



Therefore, in this section, we have seen all three in action. Now, let us discuss few important points around these three.
  1. Plain Class:

    Frankly speaking, the disadvantage of simple class or plain class is, it doesn't enforce to implement the virtual methods in the derived class. Therefore, at compile time it won't produce any error, but if by chance you call the same method in main program, then at run time it will throw exception.

  2. Abstract Class:

    It enforces to implement the abstract method in the derived class, else we will get compile time error, which is good obviously. However, Abstract classes can contain concrete methods with implementations as well. Now, in C#, one class can get inherited from only one class as c# doesn't support multiple inheritance. Members of abstract classes can have access to access modifiers like public, private, protected, etc. Also, abstract classes can contain anything like Events, Fields, Properties, Constructors, Destructors, Methods, Indexers.

  3. Interfaces:

    It also enforces compile time checking. You need to implement all the methods upfront. Interface can't have any implementations inside. It can only have declarations. Also, a class can implement multiple interfaces. Interfaces by default are public. Hence, if we try to add any access modifier to any interface member, we will get compile time error. But, interfaces can have Methods, Events, Indexers or Properties.
Code Download link:
I hope you liked this small discussion around the same. Thanks for joining me.