Saturday, June 27, 2026

C# Evolution of Interface type over course of time

Interfaces in C# started as a pure contract type and gradually gained more capabilities across language versions. Here’s a timeline of the major additions.

C# Version Interface Feature Added Example
C# 1.0 (2002) Basic interfaces Method/property/event/indexer declarations
C# 2.0 Generic interfaces IEnumerable<T>
C# 3.0 No major interface changes
C# 4.0 Generic variance out, in
C# 5–7.x Minor improvements Expression-bodied members support
C# 8.0 Default interface implementations Method body inside interface
C# 8.0 Static members allowed (with bodies) Static helper methods
C# 8.0 Access modifiers inside interface private, protected, etc.
C# 11 Static abstract members Generic math
C# 11 Static virtual members Default static behavior

1. C# 1.0 — Interfaces as pure contracts

  • Only declarations were allowed.
  • No fields, no implementation.
  • abstract methods, properties, events and indexers
  • Implementation required in implementing class/struct

interface IShape
{
    void Draw();
    int Points { get; }
}
class Circle : IShape
{
    public int Points => 0;
    public void Draw()
    {
    }
}


2. C# 2.0 — Generic interfaces

  • Interfaces became type-safe.
  • Type parameter introduced with interface type and its members

interface IRepository<T>
{
    void Add(T item);
}

Example:
IEnumerable<int>
IComparer<string>


3. C# 4.0 — Variance (in, out)

  • Allowed covariance and contravariance.
  • in and out modifier with type parameter
  • The out modifier tells that the type parameter is return type
  • The in modifier tells that the type parameter is method parameter type
  • If in or out modifier is omitted  then type parameter can be (return type)/(parameter type)
Covariant (out) → return types:
interface IProducer<out T>
{
    T Create();
}

Contravariant (in) → parameter types:
interface IConsumer<in T>
{
    void Consume(T item);
}


4. C# 8.0 — Default Interface Methods (major change)
  • Interfaces could finally contain implementations.
  • This will be default implementation for implementers(class/struct)
interface ILogger
{
    void Log(string msg)
    {
        Console.WriteLine(msg);
    }
}
Implementers can inherit default behavior:
class FileLogger : ILogger
{
}

This helped version interfaces without breaking existing code.

5. C# 8.0 — Access modifiers in interfaces
Before C# 8:
interface ITest
{
void Show(); // implicitly public
}

After C# 8:
interface ITest
{
    public void Show()
    {
    }
    private void Helper()
    {
    }
}

👉public interface members became valid only once interfaces could contain implementations.

6. C# 8.0 — Static members in interfaces

interface IMath
{
    static int Add(int a, int b)
    {
        return a + b;
    }
}

Used like: IMath.Add(10, 20);

7. C# 11 — Static abstract members (very important)

  • Interfaces can require static members.
interface IAddable<T>
{
    static abstract T operator +(T a, T b);
}

Implementation:

struct Number : IAddable<Number>
{
    public int Value;
    public static Number operator +(Number a, Number b)
    => new Number { Value = a.Value + b.Value };
}

👉This enabled generic math.

8. C# 11 — Static virtual members
interface IExample
{
    static virtual int Value => 100;
}
Allows default static behavior.

Overall evolution

  1. Contract only(abstract declarations of method/property/event/indexer)
  2. Generic contracts(introduced type parameter T) 
  3. Variance support (in and out modifiers)
  4. Interfaces with behavior (default methods and access modifier with members)
  5. Static polymorphism (generic math)
So modern C# interfaces are much more powerful than the original “only abstract members” design.

 

No comments:

Post a Comment

Hot Topics