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.
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:
C#1// Defining an abstract class 2public abstract class Shape 3{ 4 // ... 5}
This Shape
class serves as a blueprint. Subclasses can inherit from Shape
and add specific attributes and behaviors.
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.
C#1public abstract class Shape 2{ 3 // Defining abstract methods 4 public abstract double Area(); 5 public abstract double Perimeter(); 6}
Subclasses of Shape
must implement these abstract methods.
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.
C#1public class Circle : Shape 2{ 3 public double Radius { get; set; } 4 5 public Circle(double radius) 6 { 7 Radius = radius; 8 } 9 10 // Overridden method to calculate the area of the circle 11 public override double Area() 12 { 13 return Math.PI * Math.Pow(Radius, 2); 14 } 15 16 // Overridden method to calculate the perimeter of the circle 17 public override double Perimeter() 18 { 19 return 2 * Math.PI * Radius; 20 } 21}
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.
C#1public class Rectangle : Shape 2{ 3 public double Width { get; set; } 4 public double Height { get; set; } 5 6 public Rectangle(double width, double height) 7 { 8 Width = width; 9 Height = height; 10 } 11 12 // Overridden method to calculate the area of the rectangle 13 public override double Area() 14 { 15 return Width * Height; 16 } 17 18 // Overridden method to calculate the area of the rectangle 19 public override double Perimeter() 20 { 21 return 2 * (Width + Height); 22 } 23}
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.
C#1class Program 2{ 3 static void Main() 4 { 5 Circle circle = new Circle(5); 6 Rectangle rectangle = new Rectangle(4, 6); 7 8 Console.WriteLine($"Circle Area: {circle.Area()}, Perimeter: {circle.Perimeter()}"); 9 // Output: Circle Area: 78.53981633974483, Perimeter: 31.41592653589793 10 Console.WriteLine($"Rectangle Area: {rectangle.Area()}, Perimeter: {rectangle.Perimeter()}"); 11 // Output: Rectangle Area: 24, Perimeter: 20 12 13 Shape shape = new Circle(3); 14 Console.WriteLine($"Shape Area: {shape.Area()}, Perimeter: {shape.Perimeter()}"); 15 // Output: Shape Area: 28.274333882308138, Perimeter: 18.84955592153876 16 } 17}
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.
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.