Abstract Class vs Interface in C#
When designing class hierarchies in C#, you’ll often face a choice between using interfaces or abstract classes. Both serve as blueprints for other classes, but they have fundamental differences that affect how you design your application.
Let me walk you through their key differences with practical examples, so you can make informed decisions in your code architecture.
Implementation Capabilities
Abstract Classes
Abstract classes can contain a mix of implemented methods (with concrete code) and abstract methods (without implementation):
public abstract class Animal
{
// Concrete method with implementation
public void Breathe()
{
Console.WriteLine("The animal breathes");
}
// Abstract method that derived classes must implement
public abstract void MakeSound();
// Properties and fields
protected int Age { get; set; }
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
Interfaces
Interfaces traditionally only defined method signatures without implementation (though this changed slightly in C# 8.0):
public interface IMovable
{
void Move(int x, int y);
int Speed { get; set; }
}
public class Car : IMovable
{
public int Speed { get; set; }
public void Move(int x, int y)
{
Console.WriteLine($"Car moving to position ({x}, {y}) at speed {Speed}");
}
}
Note: Since C# 8.0, interfaces can include default method implementations, but interfaces still cannot contain state (instance fields).
Inheritance Restrictions
One of the most practical differences that affects your architecture decisions:
- Abstract classes: A class can inherit from only one abstract class (C# doesn’t support multiple inheritance for classes)
- Interfaces: A class can implement multiple interfaces
This gives interfaces a significant advantage when designing flexible systems:
// Multiple interface implementation
public class ModernCar : IMovable, IFuelable, IElectric
{
// Implementation of all interface members...
}
Access Modifiers
Abstract Classes
Abstract classes support the full range of access modifiers:
public abstract class Vehicle
{
public string Model { get; set; } // Public
protected int Year { get; set; } // Protected
private string _vin; // Private
internal void Register() { } // Internal
protected internal void Service() { } // Protected internal
}
Interfaces
Interface members are always implicitly public:
public interface IRepository
{
// All these are implicitly public
void Add(object item);
object GetById(int id);
IEnumerable<object> GetAll();
}
When to Use Each
Choose Abstract Classes When:
- You need to share code (not just structure) across related classes
- You want to provide a base implementation with some common functionality
- You need to use non-public members (private, protected fields/methods)
- The hierarchy represents an “is-a” relationship (Dog is an Animal)
Choose Interfaces When:
- You need a class to implement multiple contracts
- You’re defining a capability that can apply to unrelated classes
- You want to focus on what classes do, not what they are
- You’re designing for future flexibility and loose coupling
Real-World Example
Let’s see how these concepts might be applied in a real application:
// Using interfaces for capabilities that cross class hierarchies
public interface ISerializable
{
string Serialize();
void Deserialize(string data);
}
// Abstract class for a family of related classes
public abstract class DatabaseEntity
{
public int Id { get; set; }
public DateTime Created { get; set; }
protected DatabaseEntity()
{
Created = DateTime.Now;
}
public abstract void Validate();
public void Save()
{
Validate();
// Common save logic here
Console.WriteLine("Entity saved to database");
}
}
// Concrete class implementing both the interface and abstract class
public class Customer : DatabaseEntity, ISerializable
{
public string Name { get; set; }
public string Email { get; set; }
public override void Validate()
{
if (string.IsNullOrEmpty(Name))
throw new ValidationException("Customer name cannot be empty");
if (string.IsNullOrEmpty(Email) || !Email.Contains("@"))
throw new ValidationException("Invalid email address");
}
public string Serialize()
{
// Serialize the customer to JSON, XML, etc.
return $"{Id}|{Name}|{Email}|{Created}";
}
public void Deserialize(string data)
{
// Parse the serialized string
var parts = data.Split('|');
Id = int.Parse(parts[0]);
Name = parts[1];
Email = parts[2];
Created = DateTime.Parse(parts[3]);
}
}
The Bottom Line
The choice between abstract classes and interfaces isn’t about which is “better” but rather which fits your specific needs.
In modern C# development, many architects follow the “favor composition over inheritance” principle, which often leads to interface-heavy designs for flexibility. However, abstract classes remain invaluable when you need to share both behavior and state across a family of related classes.
Interfaces are particularly useful when implementing the SOLID principles, especially the Interface Segregation Principle, which recommends creating small, focused interfaces rather than large, monolithic ones.