What is the difference between abstract class and interface? When would you use each?

12 minintermediate.NETOOPinterfaces

Quick Answer

Abstract classes can have implementation, fields, constructors, and support single inheritance. Interfaces define contracts, support multiple inheritance, and (since C# 8.0) can have default implementation. Use abstract classes for shared code and 'is-a' relationships. Use interfaces for contracts and 'can-do' relationships.

Detailed Answer

Abstract Class:

  • Can have implementation (concrete methods)
  • Can have fields, properties, constructors
  • Supports access modifiers (public, protected, private)
  • Single inheritance only (a class can inherit from one abstract class)
  • Can have static members
  • Use "abstract" keyword

Interface:

  • Cannot have implementation (before C# 8.0)
  • C# 8.0+: Can have default implementation
  • Cannot have fields (can have properties)
  • All members are public by default
  • Multiple inheritance (a class can implement multiple interfaces)
  • Cannot have constructors or static members (except C# 8.0+)
  • Use "interface" keyword

Example:

// ABSTRACT CLASS
public abstract class Animal
{
    protected string name;  // Field
    
    public Animal(string name)  // Constructor
    {
        this.name = name;
    }
    
    public abstract void MakeSound();  // Abstract method - must be implemented
    
    public void Sleep()  // Concrete method - shared implementation
    {
        Console.WriteLine($"{name} is sleeping");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    
    public override void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
}

// INTERFACE
public interface IFlyable
{
    void Fly();  // No implementation
    int MaxAltitude { get; set; }  // Property
}

public interface ISwimmable
{
    void Swim();
}

public class Duck : Animal, IFlyable, ISwimmable  // Multiple interfaces!
{
    public Duck(string name) : base(name) { }
    
    public override void MakeSound()
    {
        Console.WriteLine("Quack!");
    }
    
    public void Fly()
    {
        Console.WriteLine("Duck is flying");
    }
    
    public void Swim()
    {
        Console.WriteLine("Duck is swimming");
    }
    
    public int MaxAltitude { get; set; }
}

When to use Abstract Class:

  1. You have shared code that multiple derived classes can use
  2. You need to define constructors or fields
  3. You need non-public members (protected, private)
  4. You want to provide a common base with some default behavior
  5. Related classes in an "is-a" relationship (Dog IS-A Animal)

When to use Interface:

  1. You need multiple inheritance
  2. You want to define a contract without implementation
  3. Unrelated classes need to share behavior (Duck and Airplane both implement IFlyable)
  4. You want to achieve loose coupling
  5. You need to define capabilities ("can-do" relationship)

Real-world example:

// Abstract class for shared behavior
public abstract class Vehicle
{
    public string Make { get; set; }
    public string Model { get; set; }
    
    public abstract void Start();
    
    public void DisplayInfo()  // Shared implementation
    {
        Console.WriteLine($"{Make} {Model}");
    }
}

// Interfaces for capabilities
public interface IElectric
{
    void Charge();
    int BatteryLevel { get; }
}

public interface IAutonomous
{
    void EnableAutoPilot();
    void DisableAutoPilot();
}

// Tesla combines abstract class and multiple interfaces
public class Tesla : Vehicle, IElectric, IAutonomous
{
    public int BatteryLevel { get; private set; }
    
    public override void Start()
    {
        Console.WriteLine("Tesla started silently");
    }
    
    public void Charge()
    {
        BatteryLevel = 100;
    }
    
    public void EnableAutoPilot()
    {
        Console.WriteLine("AutoPilot enabled");
    }
    
    public void DisableAutoPilot()
    {
        Console.WriteLine("AutoPilot disabled");
    }
}

C# 8.0+ Default Interface Implementation:

public interface ILogger
{
    void Log(string message);
    
    // Default implementation (C# 8.0+)
    void LogError(string message)
    {
        Log($"ERROR: {message}");
    }
}

Key Decision Factors:

  • Need shared state/fields? → Abstract Class
  • Need multiple inheritance? → Interface
  • Need constructors? → Abstract Class
  • Defining a contract? → Interface
  • Related types? → Abstract Class
  • Capabilities/behaviors? → Interface