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:

  1. You need to share code (not just structure) across related classes
  2. You want to provide a base implementation with some common functionality
  3. You need to use non-public members (private, protected fields/methods)
  4. The hierarchy represents an “is-a” relationship (Dog is an Animal)

Choose Interfaces When:

  1. You need a class to implement multiple contracts
  2. You’re defining a capability that can apply to unrelated classes
  3. You want to focus on what classes do, not what they are
  4. 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.