11.5. Abstraction

Abstraction is achieved by ignoring implementation details while focusing on essential features. Abstraction can be achieved through the use of either abstract classes or interfaces.

The most common form of abstraction in OOP is data abstraction, which means a set of abstract operations fully defines the behavior of an abstract data object. Note that, in general: [1]

  • An abstract class defines what something is (description). Abstract classes can contain implementation of methods, fields, constructors, etc.

  • An interface defines that something can do (behavior). Therefore, they only contains method and property prototypes. For example, the interface IEnumerable means that anything implements IEnumerable is enumerable and its behavior is it can be enumerated.

A class can implement multiple interfaces, but it can only inherit one class (abstract or otherwise).

11.5.1. Abstract Classes

In C#, the abstract keyword is used to create abstract classes and abstract methods:

  • Abstract class: is a restricted class that cannot be used to create objects; instead, it must be inherited from a child class to be used.

  • Abstract method: can only be used in an abstract class, and it does not have a body. The body is provided by the derived (child) class.

Important characteristics of abstract classes include:

  • Members: An abstract class can contain both abstract and non-abstract members.

  • override: Derived classes that implement abstract methods must use the override keyword.

  • Use: An abstract class cannot be instantiated using a new keyword; it must be inherited by a derived (child) class in order to be used.

  • Abstract members: Abstract members of an abstract class may include methods, properties, or events that do not have an implementation, and must be implemented in the derived class.

  • If a class contains any abstract members, it must also be marked as abstract.

  • concrete class: A derived class that implements all of the abstract members of its base class is considered to be a concrete class, and can be instantiated.

Here is an example of an abstract class that contains an abstract method in C#:

1abstract class Shape
2{
3    public abstract double GetArea();
4}

Note that, since an abstract cannot be instantiated, if you try to create an object from it:

Shape shape = new Shape();

You will receive an error message like (in macOS):

tcn85@mac:~/workspace/introcscs/Ch11OOP$ dotnet run
/Users/tcn85/workspace/introcscs/Ch11OOP/Vehicle.cs(29,23): error CS0144: Cannot create an
instance of the abstract type or interface 'Shape' [/Users/tcn85/workspace/introcscs/Ch11OOP/Ch11OOP.csproj]

The build failed. Fix the build errors and run again.

or in Windows:

PS C:\Users\tcn85\workspace\introcscs\Ch11OOP> dotnet run
C:\Users\tcn85\workspace\introcscs\Ch11OOP\Vehicle.cs(29,23): error CS0144: Cannot create an
instance of the abstract type or interface 'Shape' [C:\Users\tcn85\workspace\introcscs\Ch11OOP\Ch11OOP.csproj]

The build failed. Fix the build errors and run again.
PS C:\Users\tcn85\workspace\introcscs\Ch11OOP>

This abstract class represents a shape, and has a single abstract method called GetArea() that returns the area of the shape. From Shape, you can define different classes inheriting Shape (such as Circle, Square, Triangle…). Each child class would implement the GetArea() method differently since this method is abstract and therefore it does not have an implementation and must be implemented in a derived class.

Here is an example of a derived class that implements the GetArea() method:

class Circle : Shape
{
    private double radius;                  // field (data)

    public Circle(double radius)            // a constructor intaking argument double radius
    {
        this.radius = radius;               // create the variable to be used in the object
    }

    public override double GetArea()        // implementation of the GetArea method from Shape; override
    {
        return Math.PI * radius * radius;
    }
}


class Program
{
    static void Main(string[] args)
    {
        Circle circle = new Circle(10);
        double area = circle.GetArea();
        Console.WriteLine(area);            // output: 314.1592653589793
    }
}

As another example, see the abstract class and methods defined and executed as follows.

 1abstract class Animal                       // Abstract class
 2{
 3    public abstract void animalSound();     // Abstract method (does not have a body)
 4
 5    public void sleep()                     // Regular method
 6    {
 7        Console.WriteLine("Zzz");
 8    }
 9}
10
11
12class Pig : Animal                          // Derived class (inherit from Animal)
13{
14    public override void animalSound()      // method implementation body of animalSound(); override
15    {
16        Console.WriteLine("The pig says: wee wee");
17    }
18}
19
20
21class Program
22{
23    static void Main(string[] args)
24    {
25        Pig myPig = new Pig();              // Create a Pig object
26        myPig.animalSound();                // Call the implemented abstract method:
27                                            // output The pig says: wee wee
28        myPig.sleep();                      // Call the regular method (in base class)
29    }                                       // output: Zzz
30
31}

Abstraction is a powerful modular design that allows for a separation of concerns between the interface/abstract class and the implementation of a class. It helps to reduce complexity and improve maintainability by allowing changes to be made to the implementation without affecting the overall behavior of the class. In this example below, you see that this time a Rectangle class is created to inherit the abstract class Shape and two abstract methods are implemented.

 1abstract class Shape
 2{
 3    public abstract double GetArea();
 4    public abstract double GetPerimeter();
 5}
 6
 7class Rectangle : Shape
 8{
 9    private double width;
10    private double height;
11
12    public Rectangle(double width, double height)
13    {
14        this.width = width;
15        this.height = height;
16    }
17
18    public override double GetArea()
19    {
20        return width * height;
21    }
22
23    public override double GetPerimeter()
24    {
25        return 2 * (width + height);
26    }
27}

11.5.2. Interfaces

Interface is another way of achieving abstraction in C#. An interface is a completely “abstract” class, which can only contain abstract methods and properties (with empty bodies) without any implementation. For example, the following code is an interface definition that does not have any method implementation (body, {}): [2]

interface Animal
{
    void animalSound();     ///// interface method (does not have a body)
    void run();             ///// interface method (does not have a body)
}

Note that:

  1. It is considered good practice to start with the letter “I” at the beginning of an interface name to note that it is an interface rather than a class.

  2. Interfaces can contain properties and methods, but not fields.

  3. By default, members of an interface are abstract and public.

  4. To use the interface methods, the interface must be “implemented” (kinda like inherited) by a child class using the : symbol (just like with inheritance).

  5. You do not have to use the override keyword when implementing an interface.

For example:

 1interface IAnimal
 2{
 3  void animalSound();       // interface method (does not have a body)
 4}
 5
 6
 7class Pig : IAnimal         // Pig "implements" the IAnimal interface
 8{
 9  public void animalSound()
10  {                         // The body of animalSound() is provided here
11    Console.WriteLine("The pig says: wee wee");
12  }
13}
14
15class Program
16{
17  static void Main(string[] args)
18  {
19    Pig myPig = new Pig();  // Create a Pig object
20    myPig.animalSound();    // use the implemented abstract method
21  }
22}

11.5.3. Difference Between Abstract Class and Interface in C#

There are key features in abstract class and interface that you would need to get familiar with in future learning. For the purpose of this course, learning how to use the techniques and understanding the ideas behind both is sufficient. Given the similarity in nature between the two OOP techniques, it help to see a comparison of abstract class and interface as below. [3]

Abstract Class vs. Interface :widths: 20 30 30 20 :header-rows: 1

Feature

Abstract Class

Interface

.

Inheritance

Can only be inherited from one class

Can implement multiple interfaces

Default Implementation

Can provide a default implementation

Cannot provide a default implementation (until C# 8.0, where default methods are allowed)

Access Modifiers

Can have access modifiers for its members

Members are public by default

Constructors

Can have constructors

Cannot have constructors

Fields

Can contain fields

It cannot contain fields

Method Definition

Can have defined, abstract, or virtual methods

Only contains method signatures (until C# 8.0)

State or Data

Can hold state (fields)

Cannot hold state

Inheritance vs Implementation

Inherits from another class (abstract or concrete)

Implements an interface

Type of Methods

Can have static, abstract, virtual methods

Only abstract methods (until C# 8.0)

Compatibility

Suitable for classes with closely related functionality

Suitable for classes with unrelated functionalities

Versioning

Easier to add new methods without breaking existing implementations

Adding new methods can break existing implementations

Footnotes