Understanding Abstraction

Welcome back! Previously, you delved into polymorphism and learned how to create more flexible code structures using classes and inheritance. In this session, we will explore a crucial aspect of Object-Oriented Programming in C#: abstract classes and abstract methods.

What is an Abstract Class?

An abstract class in C# is a class that cannot be instantiated directly. Think of it as a blueprint for other classes. It can include abstract methods that are declared without implementation and non-abstract methods that have implementation. However, an abstract class can also be entirely free of methods initially.

To illustrate this, let's create an abstract class using the abstract keyword:

// Defining an abstract class
public abstract class Shape
{
    // ...
}

This Shape class serves as a blueprint. Subclasses can inherit from Shape and add specific attributes and behaviors.

Implementing Abstract Methods

Now that you understand what an abstract class is, let's add abstract methods to our Shape class. An abstract method acts as a placeholder without any implementation, serving as a rule that subclasses must follow. It enforces consistency across subclasses while allowing each to fulfill the required behavior uniquely.

public abstract class Shape
{
    // Defining abstract methods
    public abstract double Area();
    public abstract double Perimeter();
}

Subclasses of Shape must implement these abstract methods.

Concrete Class: Circle

To see abstract methods in action, let's create a concrete class called Circle that inherits from Shape. The Circle class uses its constructor to initialize the radius and overrides both methods to provide specific implementations for calculating its area and perimeter.

public class Circle : Shape
{
    public double Radius { get; set; }

    public Circle(double radius)
    {
        Radius = radius;
    }

    // Overridden method to calculate the area of the circle
    public override double Area()
    {
        return Math.PI * Math.Pow(Radius, 2);
    }

    // Overridden method to calculate the perimeter of the circle
    public override double Perimeter()
    {
        return 2 * Math.PI * Radius;
    }
}
Concrete Class: Rectangle

Similarly, we can create another concrete class called Rectangle that also inherits from Shape. The Rectangle class initializes its width and height in the constructor and implements methods to compute its area and perimeter.

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }

    // Overridden method to calculate the area of the rectangle
    public override double Area()
    {
        return Width * Height;
    }
    
    // Overridden method to calculate the area of the rectangle
    public override double Perimeter()
    {
        return 2 * (Width + Height);
    }
}
Using Abstract Classes

To see how abstract classes are used in practice, let’s examine the Main method. Here, we create instances of Circle and Rectangle, and their areas and perimeters are printed to the console. This demonstrates the concrete implementations in each subclass.

class Program
{
    static void Main()
    {
        Circle circle = new Circle(5);
        Rectangle rectangle = new Rectangle(4, 6);

        Console.WriteLine($"Circle Area: {circle.Area()}, Perimeter: {circle.Perimeter()}");
        // Output: Circle Area: 78.53981633974483, Perimeter: 31.41592653589793
        Console.WriteLine($"Rectangle Area: {rectangle.Area()}, Perimeter: {rectangle.Perimeter()}");
        // Output: Rectangle Area: 24, Perimeter: 20

        Shape shape = new Circle(3);
        Console.WriteLine($"Shape Area: {shape.Area()}, Perimeter: {shape.Perimeter()}");
        // Output: Shape Area: 28.274333882308138, Perimeter: 18.84955592153876
    }
}

Note that we can use a reference to the Shape class to hold instances of its subclasses, demonstrating polymorphism and allowing for more flexible, reusable code. Additionally, the Shape class itself cannot be instantiated directly; only its subclasses with concrete method implementations can be. This ensures a consistent interface, improving code maintainability and readability.

Conclusion

Abstract classes and methods are powerful tools in C#'s OOP arsenal. They allow you to define a blueprint for a group of related classes, ensuring that all subclasses follow a specific interface. This promotes consistency and reliability in your code. By mastering abstract base classes and abstract methods, you can create more organized, maintainable, and scalable software.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal