C# interfaces — define, implement, explicit implementation, and interface vs 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.
Defining an Interface
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
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.
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×3Implementing Multiple Interfaces
A class can implement any number of interfaces (unlike inheritance, which is limited to one base class).
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.
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.
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 fullInterfaces with Generics
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
| Interface | Abstract class | |
|---|---|---|
| Multiple inheritance | Yes — a class can implement many | No — single base class only |
| Fields | No | Yes |
| Constructors | No | Yes |
| Default implementations | Yes (C# 8+) | Yes |
| State | No | Yes |
| Access modifiers on members | Limited (public by default) | Full control |
| Use when | Defining a capability/contract | Sharing implementation between related types |
// 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
object obj = new Dog("Rex");
// is pattern
if (obj is IAnimal animal)
animal.Speak();
// as cast
ITrainable? trainable = obj as ITrainable;
trainable?.Train("Sit");