Reading — step 1 of 7
Learn
Interfaces define contracts — what a type can do, without saying how. C# 8+ added DEFAULT INTERFACE METHODS, but the core idea is unchanged: any class can implement multiple interfaces; this is C#'s answer to multiple inheritance.
Basic interface
public interface IGreeter
{
string Name { get; }
string Greet();
}
public class Person : IGreeter
{
public string Name { get; }
public Person(string name) { Name = name; }
public string Greet() => $"Hi, I'm {Name}";
}
Classes implement an interface by listing it after :. The compiler checks that every member is provided.
Convention: interface names start with I (IEnumerable, IDisposable, IComparable).
Multiple interfaces
public class Logger : IGreeter, IDisposable
{
public string Name { get; } = "Logger";
public string Greet() => "Hello from logger";
public void Dispose() { /* cleanup */ }
}
A class can implement any number of interfaces. IDisposable and IEnumerable are common — many BCL types implement them.
Properties on interfaces
public interface ICountable
{
int Count { get; } // read-only on the interface
}
public class Container : ICountable
{
public int Count { get; private set; } // implementer can have a setter
}
The interface declares a get-only property. The implementer can add a setter; the interface doesn't restrict it.
Default interface methods (C# 8+)
public interface ILogger
{
void Log(string message);
void LogInfo(string msg) => Log($"INFO: {msg}");
void LogError(string msg) => Log($"ERROR: {msg}");
}
The LogInfo and LogError methods have default bodies. Implementers only need to provide Log.
Default methods came late to C# (C# 8 / 2019). Different from Java 8 (2014). Used cautiously — they're great for evolving interfaces without breaking existing implementers, but power-tooling-only.
Explicit interface implementation
public class Session : IDisposable
{
void IDisposable.Dispose() { /* ... */ } // ONLY accessible via IDisposable reference
}
var session = new Session();
// session.Dispose(); // ERROR — not on Session directly
((IDisposable)session).Dispose(); // OK
Use when:
- Two interfaces have conflicting member names
- You want the interface method hidden from the class's public surface
- Disambiguating implementations
Common BCL interfaces
IEnumerable<T>— forforeachIDisposable— forusingIComparable<T>— forSortIEquatable<T>— for==INotifyPropertyChanged— for data bindingIList<T>,ICollection<T>,IDictionary<TKey, TValue>— collection protocols
Most C# code consumes (rather than defines) interfaces. When defining your own, follow the same I prefix convention.
When to use interface vs abstract class
- Interface — contract; multiple inheritance; no state (except in C# 8+ default methods, no fields ever)
- Abstract class — partial implementation with state, single inheritance, lifecycle hooks
Common pattern: an interface defines the contract, an abstract base provides shared default behavior:
public interface IShape { double Area(); }
public abstract class ShapeBase : IShape
{
public abstract double Area();
public string Describe() => $"Shape with area {Area()}";
}
Common mistakes
- Forgetting interface members — the class won't compile until everything is provided.
- Confusing implicit and explicit interface implementation — explicit hides the member from the class type.
- Using interfaces with state expectations — interfaces traditionally have no fields. Default methods can call other interface members but can't store data.
- Putting too much in an interface — Interface Segregation Principle: prefer many small interfaces over one bloated one.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…