Abstract Classes
Vaibhav • September 10, 2025
In the previous article, we explored how constructors behave in inheritance hierarchies. Now we’re ready to take a step further into the world of object-oriented design by introducing abstract classes. These are special classes that cannot be instantiated directly but serve as blueprints for other classes. They allow you to define a common contract while leaving some implementation details to derived classes.
What is an abstract class?
An abstract class is a class that is declared with the abstract
keyword. It may
contain abstract members (methods or properties without implementation) as well as fully implemented members.
You cannot create an object of an abstract class directly - it’s meant to be inherited.
abstract class Animal
{
public abstract void Speak(); // No implementation
public void Eat()
{
Console.WriteLine("Eating...");
}
}
In this example, Animal
defines a method Speak()
that must be implemented by any derived class. But it also provides a
concrete method Eat()
that all animals can use as-is.
Abstract classes are ideal when you want to enforce a common interface while sharing some default behavior across derived types.
Why use abstract classes?
Abstract classes help you model real-world hierarchies where some behaviors are shared, and others are specific. For example, all vehicles have a way to start, but the exact mechanism differs between a car and a bicycle. Abstract classes let you define the shared structure and force derived classes to fill in the details.
abstract class Vehicle
{
public abstract void Start();
public void Stop()
{
Console.WriteLine("Vehicle stopped.");
}
}
class Car : Vehicle
{
public override void Start()
{
Console.WriteLine("Car engine started.");
}
}
Here, Car
must implement Start()
, but it
inherits Stop()
from Vehicle
without needing
to redefine it.
Abstract vs. non-abstract members
An abstract class can contain both abstract and non-abstract members. Abstract members act like placeholders - they define the method signature but not the body. Non-abstract members provide reusable logic.
abstract class Shape
{
public abstract double Area(); // Must be implemented
public void Describe()
{
Console.WriteLine("This is a shape.");
}
}
Any class that derives from Shape
must implement Area()
, but it can use Describe()
as-is.
You cannot mark a method as abstract
unless the
class itself is also marked abstract
.
Implementing abstract classes
When you inherit from an abstract class, you must implement all its abstract members unless your derived class is also abstract. This ensures that any concrete (non-abstract) class is fully functional and can be instantiated.
abstract class Appliance
{
public abstract void TurnOn();
public abstract void TurnOff();
}
class Fan : Appliance
{
public override void TurnOn()
{
Console.WriteLine("Fan is now on.");
}
public override void TurnOff()
{
Console.WriteLine("Fan is now off.");
}
}
The Fan
class provides concrete implementations for both abstract methods. Now
it can be instantiated and used like any regular class.
Abstract properties
Just like methods, properties can also be abstract. This is useful when you want to enforce that a derived class exposes certain data, but you don’t want to dictate how it’s stored or computed.
abstract class Employee
{
public abstract string Name { get; }
}
class Manager : Employee
{
public override string Name => "Aparna Rao";
}
The Manager
class must provide a concrete implementation of the Name
property. This pattern is common in frameworks where base classes define
contracts for data access.
Abstract classes vs interfaces
Both abstract classes and interfaces define contracts, but they serve different purposes. Abstract classes can include implementation and state (fields), while interfaces cannot. Use abstract classes when you want to share code; use interfaces when you want to enforce a contract across unrelated types.
interface IPrintable
{
void Print();
}
abstract class Document
{
public abstract void Save();
public void Open()
{
Console.WriteLine("Opening document...");
}
}
class Report : Document, IPrintable
{
public override void Save()
{
Console.WriteLine("Saving report...");
}
public void Print()
{
Console.WriteLine("Printing report...");
}
}
Here, Report
inherits behavior from Document
and also commits to the IPrintable
contract. This combination is powerful and
common in real-world applications.
Use abstract classes when you want to provide shared logic and enforce a contract. Use interfaces when you only need the contract and want to support multiple inheritance.
Constructor behavior in abstract classes
Abstract classes can have constructors, and these constructors are called when a derived class is instantiated. This allows the base class to initialize shared state or enforce setup logic.
abstract class Logger
{
protected Logger()
{
Console.WriteLine("Logger initialized.");
}
public abstract void Log(string message);
}
class FileLogger : Logger
{
public override void Log(string message)
{
Console.WriteLine($"Writing to file: {message}");
}
}
When you create a FileLogger
, the Logger
constructor runs first. This is consistent with the constructor chaining rules we covered earlier.
Combining abstract and virtual members
Abstract members must be overridden, but virtual members can be optionally overridden. This gives you
flexibility: use abstract
when you want to force implementation, and virtual
when you want to provide a default that can be changed.
abstract class Notification
{
public abstract void Send();
public virtual void Log()
{
Console.WriteLine("Notification sent.");
}
}
class EmailNotification : Notification
{
public override void Send()
{
Console.WriteLine("Sending email...");
}
public override void Log()
{
Console.WriteLine("Email logged.");
}
}
The EmailNotification
class must implement Send()
, but it can choose whether or not to override Log()
.
You can mix abstract, virtual, and concrete members in the same abstract class. This allows you to define a rich and flexible base for your object hierarchy.
Abstract classes in real-world design
Abstract classes are widely used in frameworks and libraries. For example, ASP.NET MVC uses abstract controllers, and many UI frameworks define abstract base classes for components. They help enforce consistency while allowing customization.
Suppose you’re building a drawing app. You might define an abstract class Tool
with abstract methods like OnMouseDown()
and OnMouseMove()
. Each tool (pen, eraser, shape) would implement these differently,
but the app can treat them all as Tool
objects.
Summary
Abstract classes are foundational to object-oriented design in C#. They let you define a common contract while sharing reusable logic. You’ve learned how to declare abstract classes and members, how to implement them in derived classes, how they differ from interfaces, and how they fit into real-world design patterns. You also saw how constructors and virtual methods interact with abstract classes to create flexible and powerful hierarchies.
In the next article, we’ll explore Sealed Classes - how to prevent further inheritance and why that can be useful in certain scenarios.