Back to Home

C# Interface: Define, Implement, and Compare with an Abstract Class

An interface defines a contract — a set of members that any implementing type must provide. Interfaces enable polymorphism and dependency injection without requiring inheritance from a common base class.

Quick answer: use an interface to define capabilities that unrelated classes can share. Use an abstract class when implementations need shared state or common base behavior.

Interface vs Abstract Class

Use an interface when...Use an abstract class when...
Unrelated types share a capabilityTypes share state or implementation
A class needs to implement multiple contractsA type should inherit one common base
You want a small dependency-injection contractYou need protected members or constructors

Defining an Interface

C# Example Code
public interface IAnimal
{
    string Name { get; }
    void Speak();
}

By convention, interface names start with I. Members are public by default — no access modifiers needed.

Implementing an Interface

C# Example Code
public interface IAnimal
{
    string Name { get; }
    void Speak();
}

public class Dog : IAnimal
{
    public string Name { get; }

    public Dog(string name) => Name = name;

    public void Speak() => Console.WriteLine($"{Name} says: Woof!");
}

public class Cat : IAnimal
{
    public string Name { get; }

    public Cat(string name) => Name = name;

    public void Speak() => Console.WriteLine($"{Name} says: Meow!");
}

IAnimal[] animals = { new Dog("Rex"), new Cat("Whiskers") };
foreach (var a in animals)
    a.Speak();

// Rex says: Woof!
// Whiskers says: Meow!

Interface Properties and Methods

Interfaces can declare properties, methods, indexers, and events — but not fields.

C# Example Code
public interface IShape
{
    double Area    { get; }          // property
    double Perimeter { get; }        // property
    string Describe();               // method
}

public class Rectangle : IShape
{
    public double Width  { get; }
    public double Height { get; }

    public Rectangle(double width, double height)
    {
        Width  = width;
        Height = height;
    }

    public double Area      => Width * Height;
    public double Perimeter => 2 * (Width + Height);
    public string Describe() => $"Rectangle {Width}×{Height}";
}

IShape s = new Rectangle(4, 3);
Console.WriteLine(s.Area);       // 12
Console.WriteLine(s.Describe()); // Rectangle 4×3

Implementing Multiple Interfaces

A class can implement any number of interfaces (unlike inheritance, which is limited to one base class).

C# Example Code
public interface IReadable
{
    string Read();
}

public interface IWritable
{
    void Write(string value);
}

public interface IReadWritable : IReadable, IWritable { }  // interface inheritance

public class Buffer : IReadWritable
{
    private string _data = "";

    public string Read()              => _data;
    public void   Write(string value) => _data = value;
}

var buf = new Buffer();
buf.Write("Hello!");
Console.WriteLine(buf.Read()); // Hello!

Explicit Interface Implementation

Use explicit implementation when two interfaces have conflicting member names, or to hide a member from the default view of the class.

C# Example Code
public interface IEnglishGreeter
{
    string Greet();
}

public interface ISpanishGreeter
{
    string Greet();
}

public class Bilingual : IEnglishGreeter, ISpanishGreeter
{
    // Explicit — accessible only via the interface type
    string IEnglishGreeter.Greet() => "Hello!";
    string ISpanishGreeter.Greet() => "¡Hola!";
}

var b = new Bilingual();
// b.Greet(); // Compile error — ambiguous

IEnglishGreeter en = b;
ISpanishGreeter es = b;
Console.WriteLine(en.Greet()); // Hello!
Console.WriteLine(es.Greet()); // ¡Hola!

Default Interface Methods (C# 8+)

Interfaces can now include method implementations. This lets you add new members to an interface without breaking existing implementors.

C# Example Code
public interface ILogger
{
    void Log(string message);

    // Default implementation — optional to override
    void LogError(string message) => Log($"[ERROR] {message}");
    void LogInfo(string message)  => Log($"[INFO]  {message}");
}

public class ConsoleLogger : ILogger
{
    // Only required member
    public void Log(string message) => Console.WriteLine(message);

    // LogError and LogInfo are inherited from the interface
}

ILogger logger = new ConsoleLogger();
logger.LogInfo("App started");   // [INFO]  App started
logger.LogError("Disk full");    // [ERROR] Disk full

Interfaces with Generics

C# Example Code
public interface IRepository<T>
{
    T?   GetById(int id);
    void Save(T item);
    IEnumerable<T> GetAll();
}

public class InMemoryProductRepository : IRepository<Product>
{
    private readonly List<Product> _store = new();

    public Product? GetById(int id) => _store.FirstOrDefault(p => p.Id == id);
    public void Save(Product item)  => _store.Add(item);
    public IEnumerable<Product> GetAll() => _store.AsReadOnly();
}

record Product(int Id, string Name);

Interface vs Abstract Class

InterfaceAbstract class
Multiple inheritanceYes — a class can implement manyNo — single base class only
FieldsNoYes
ConstructorsNoYes
Default implementationsYes (C# 8+)Yes
StateNoYes
Access modifiers on membersLimited (public by default)Full control
Use whenDefining a capability/contractSharing implementation between related types
C# Example Code
// Abstract class — shared state and logic
public abstract class Animal
{
    public string Name { get; }
    protected Animal(string name) => Name = name;

    public abstract void Speak(); // must override
    public virtual  void Sleep()  => Console.WriteLine($"{Name} sleeps."); // optional override
}

// Interface — capability, no shared state
public interface ITrainable
{
    void Train(string command);
}

public class Dog : Animal, ITrainable
{
    public Dog(string name) : base(name) { }

    public override void Speak() => Console.WriteLine($"{Name}: Woof!");
    public void Train(string command) => Console.WriteLine($"{Name} learned: {command}");
}

Checking Interface Implementation at Runtime

C# Example Code
object obj = new Dog("Rex");

// is pattern
if (obj is IAnimal animal)
    animal.Speak();

// as cast
ITrainable? trainable = obj as ITrainable;
trainable?.Train("Sit");

Take It Further

The C# Player's Guide book cover

The community's favorite C# guide — teaches every concept you just read about with clarity.

The C# Player's Guide — 5th Edition · RB Whitaker

Get it on Amazon →

As an Amazon Associate I earn from qualifying purchases.