
Confused about C# interface vs abstract class? Learn the key differences, real code examples, and when to use each. Start writing cleaner C# code today!
If you've spent any time writing object-oriented code, you've hit the classic question: C# interface vs abstract class — which one should you use? It's one of the most common C# interview questions and one of the most misunderstood design decisions in real projects. Choosing the wrong abstraction leads to brittle code, painful refactors, and tight coupling. In this tutorial, you'll learn the exact difference between interface and abstract class in C#, see runnable code examples for both, and walk away with a simple decision rule you can apply immediately.
What Is an Interface in C#?
An interface is a pure contract. It declares what a type can do without saying how it does it. Any class or struct that implements the interface promises to provide an implementation for every member the contract requires. Interfaces describe a capability — like "this thing can be saved," "this thing can be compared," or "this thing can be logged to."
public interface IPaymentProcessor
{
bool Charge(decimal amount);
void Refund(decimal amount);
}
public class StripeProcessor : IPaymentProcessor
{
public bool Charge(decimal amount)
{
Console.WriteLine($"Charging {amount:C} via Stripe");
return true;
}
public void Refund(decimal amount)
{
Console.WriteLine($"Refunding {amount:C} via Stripe");
}
}
The key insight: StripeProcessor, PayPalProcessor, and SquareProcessor can each implement IPaymentProcessor in completely different ways. Your application code depends only on the contract, not on any concrete class. This is the foundation of dependency injection and testable, loosely coupled C# code.
What Is an Abstract Class in C#?
An abstract class is a partially-built base class. You cannot instantiate it directly with new, but it can contain real, working code (concrete methods, fields, constructors, properties) and abstract members that derived classes must override. Abstract classes describe what something is — a shared identity and shared behavior across a family of related types.
public abstract class Employee
{
// Shared state — interfaces can't have instance fields
protected string Name { get; }
protected decimal BaseSalary { get; }
protected Employee(string name, decimal baseSalary)
{
Name = name;
BaseSalary = baseSalary;
}
// Concrete, shared behavior inherited by every subclass
public void PrintBadge() => Console.WriteLine($"Employee: {Name}");
// Each subclass MUST define how pay is calculated
public abstract decimal CalculatePay();
}
public class SalariedEmployee : Employee
{
public SalariedEmployee(string name, decimal baseSalary)
: base(name, baseSalary) { }
public override decimal CalculatePay() => BaseSalary / 12m;
}
public class Contractor : Employee
{
private readonly int _hours;
private readonly decimal _rate;
public Contractor(string name, int hours, decimal rate)
: base(name, 0)
{
_hours = hours;
_rate = rate;
}
public override decimal CalculatePay() => _hours * _rate;
}
Notice what the abstract class gives you that an interface traditionally couldn't: a constructor, protected fields, and a fully-implemented PrintBadge() method that every subclass inherits for free. This is the power of an abstract class — sharing implementation, not just a contract.
C# Interface vs Abstract Class: The Key Differences
Here is the practical side-by-side comparison developers actually need when deciding when to use interface vs abstract class in C#:
- Multiple inheritance: A class can implement many interfaces, but can inherit from only one abstract class. This single rule alone often decides the design.
- Implementation: Abstract classes can contain full method bodies and state (fields). Interfaces are primarily contracts (with a modern exception — see below).
- Constructors: Abstract classes can have constructors to initialize shared state. Interfaces cannot.
- Access modifiers: Abstract class members can be
protected,private, etc. Interface members are implicitlypublic. - Relationship modeled: An interface models a "can-do" capability (IDisposable, IComparable). An abstract class models an "is-a" identity (a Contractor is an Employee).
- Versioning: Adding a member to an interface historically broke every implementer. Adding a concrete method to an abstract class does not.
Default Interface Methods: The Line Has Blurred
Since C# 8.0, interfaces can include default implementations. This narrowed the historic gap between the two and is a frequent follow-up in modern interviews, so it's worth understanding.
public interface ILogger
{
void Log(string message);
// Default implementation — implementers get this for free
void LogError(string message) => Log($"ERROR: {message}");
}
public class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine(message);
// LogError is inherited from the interface, no override needed
}
Default interface methods let you add new members to a published interface without breaking existing implementers — solving the old versioning problem. But be careful: interfaces still cannot hold instance state (fields). If you need shared data plus shared behavior, an abstract class is still the right tool.
When to Use an Interface vs Abstract Class in C#
Here's the decision rule I give every developer on my team. Reach for an interface when:
- Unrelated classes need to share a capability (a
FileStreamand aDbConnectionare bothIDisposablebut share no ancestry). - You need a type to implement multiple contracts at once.
- You're designing for dependency injection, mocking, and unit testing — interfaces are trivial to fake.
- You want maximum flexibility and minimum coupling.
Reach for an abstract class when:
- You have a family of closely related types that share real implementation and state.
- You want to provide a partial, reusable base that subclasses extend.
- You need constructors, protected members, or non-public helpers.
- You want to enforce a common base identity (every shape is a
Shape).
A widely used pattern combines both: expose an interface as the public contract, and provide an abstract base class that partially implements it for convenience. The .NET Base Class Library does this constantly — for example Stream is an abstract class, while contracts like IEnumerable<T> are interfaces.
public interface IShape
{
double Area();
string Describe();
}
// Abstract base supplies shared behavior; implements the interface
public abstract class ShapeBase : IShape
{
public abstract double Area();
// Free, shared implementation for every shape
public string Describe() => $"{GetType().Name} with area {Area():F2}";
}
public class Circle : ShapeBase
{
private readonly double _radius;
public Circle(double radius) => _radius = radius;
public override double Area() => Math.PI * _radius * _radius;
}
Common Pitfalls and Best Practices
- Don't default to abstract classes for everything. Because C# only allows single inheritance, an abstract base "uses up" your one inheritance slot. Interfaces keep your options open.
- Avoid the "fat interface." Follow the Interface Segregation Principle — many small, focused interfaces beat one giant one. A class shouldn't be forced to implement methods it doesn't need.
- Prefer interfaces for testability. Mocking frameworks like Moq and NSubstitute work most cleanly against interfaces, making your unit tests faster and simpler.
- Don't put business logic in default interface methods just because you can. They're best for backward-compatible additions and thin convenience helpers, not for holding core state-dependent logic.
- Use abstract classes to remove duplication. If three subclasses copy the same code, that's a signal to lift it into an abstract base.
Conclusion: Key Takeaways
The C# interface vs abstract class decision comes down to one question: are you sharing a contract or sharing an implementation? Use an interface to define a capability that unrelated types can adopt, to enable multiple inheritance of behavior, and to write loosely coupled, testable code. Use an abstract class when related types share real state and implementation and you want a strong common base identity.
Remember the essentials: a class can implement many interfaces but inherit only one abstract class; abstract classes can hold constructors, fields, and full method bodies; and since C# 8.0, default interface methods have blurred the line but still can't carry instance state. When in doubt, start with an interface for flexibility, and introduce an abstract base only when shared implementation demands it. Master this distinction and your C# architecture will be cleaner, more flexible, and far easier to maintain. Now open your IDE and refactor one tightly-coupled class to depend on an interface — it's the fastest way to make the concept stick.
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.
Comments
Post a Comment