Abstract Classes
An abstract class is a class marked 'abstract' that cannot be instantiated directly with 'new' — it exists purely to be derived from. It sits between a fully concrete class (which you can instantiate and which implements everything) and an interface (which historically implemented nothing): an abstract class can mix concrete members with shared implementation and abstract members that have no body at all, forcing every non-abstract derived class to supply one. This makes abstract classes the natural tool when a family of related types shares real code and state but also needs each concrete type to fill in some behavior of its own.
Cricket analogy: A national cricket academy's 'Batting Technique' manual can't step onto the field and bat itself — like an abstract class, it provides shared drills (grip, stance) but leaves the actual shot-making to Kohli, Root, or Smith to implement.
Declaring abstract members
A member marked 'abstract' inside an abstract class has a signature but no implementation — no body at all, ending with a semicolon — and is implicitly virtual, so derived classes must use 'override' to supply one. Abstract members can only exist inside abstract classes (or interfaces); a non-abstract class must implement every abstract member it inherits, or itself be declared abstract, passing the obligation further down the hierarchy.
Cricket analogy: A coaching manual lists 'execute a cover drive' as a required skill with no fixed technique described — each batter, from Tendulkar to Kohli, must override it with their own footwork and bat swing.
public abstract class PaymentProcessor
{
// Concrete, shared implementation
public void ProcessOrder(decimal amount)
{
Validate(amount);
Charge(amount);
Console.WriteLine($"Processed {amount:C} via {ProviderName}");
}
protected virtual void Validate(decimal amount)
{
if (amount <= 0) throw new ArgumentOutOfRangeException(nameof(amount));
}
// Abstract members — every concrete subclass must supply these
protected abstract void Charge(decimal amount);
public abstract string ProviderName { get; }
}
public class StripeProcessor : PaymentProcessor
{
public override string ProviderName => "Stripe";
protected override void Charge(decimal amount) => Console.WriteLine("Charging via Stripe API...");
}Why abstract classes cannot be instantiated
Because an abstract class may contain members with no implementation, creating an instance of it directly would leave callers able to invoke a method that literally has no code to run — the CLR and compiler forbid this outright with 'new PaymentProcessor()' failing to compile. You can still declare a variable of the abstract type and assign it any concrete derived instance, and you can still define constructors, static members, and fully concrete methods on an abstract class; those constructors only run as part of constructing a derived, concrete instance via chaining.
Cricket analogy: You can't send a 'generic batter' out to the crease — the umpire would reject it, just as 'new PaymentProcessor()' fails to compile — but a scorecard slot can still be labeled 'Batter' and filled by concrete players like Root.
Abstract classes vs. interfaces, revisited
Abstract classes support constructors, instance fields, access modifiers other than public, and — critically — only single inheritance, so a type can extend just one abstract class. Interfaces support multiple implementation per type and (since C# 8) default methods, but still cannot hold instance fields. A good rule of thumb: use an abstract base class to share both implementation and state within a genuine 'is-a' family (like the PaymentProcessor example, where ProcessOrder's control flow is identical for every provider); use interfaces to describe capabilities that can cut across unrelated families.
Cricket analogy: A player can only ever be officially registered under one national team (India, not also Australia) — like single inheritance — but can hold many cross-cutting badges like 'Vice-Captain' or 'DRS-eligible', like implementing several interfaces.
The PaymentProcessor example above is the Template Method design pattern: the abstract class defines the overall algorithm's skeleton (ProcessOrder) while deferring specific steps (Charge) to subclasses — a textbook use case for abstract classes over interfaces.
Forgetting to implement even one inherited abstract member in a concrete (non-abstract) derived class produces a compiler error, not a runtime failure — but if the derived class is itself marked abstract, the obligation silently passes down to whichever class eventually becomes concrete, which can be easy to lose track of in deep hierarchies.
- An abstract class cannot be instantiated directly; it exists to be derived from.
- Abstract members declare a signature with no body and must be overridden by concrete derived classes.
- Abstract classes can mix concrete (shared) implementation with abstract (must-implement) members.
- A class can inherit from only one abstract (or any) base class, unlike interfaces which allow many.
- Abstract classes can hold constructors, fields, and non-public members — interfaces (pre-default-methods) could not.
- The Template Method pattern — a fixed algorithm skeleton with pluggable steps — is a classic abstract class use case.
Practice what you learned
1. Can you write 'new PaymentProcessor()' if PaymentProcessor is declared abstract?
2. What happens if a concrete (non-abstract) class fails to implement an inherited abstract member?
3. How many abstract (or any) classes can a single C# class directly inherit from?
4. Which of these can an abstract class contain that distinguishes it from a classic (pre-C# 8) interface?
5. In the Template Method pattern, what role does the abstract class typically play?
Was this page helpful?
You May Also Like
Interfaces in C#
Understand interfaces as pure behavioral contracts that any type can implement, including default interface methods, explicit implementation, and multiple interface inheritance.
Inheritance in C#
Learn how C# classes derive from a single base class to reuse and extend behavior, including virtual/override methods, base member access, and sealing.
Polymorphism Explained
See how C# achieves polymorphism through virtual method dispatch, interface implementations, and method/operator overloading, letting one code path handle many shapes of data.
Encapsulation and Access Modifiers
Understand how C#'s access modifiers — public, private, protected, internal, and their combinations — let you control visibility and enforce encapsulation boundaries.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics