接口是什么

契约:声明"实现我的类必须提供这些方法",但不提供实现。

public interface IRepository<T>
{
    T? GetById(int id);
    IEnumerable<T> GetAll();
    void Save(T item);
    void Delete(int id);
}
public class UserRepo : IRepository<User>
{
    public User? GetById(int id) => ...;
    public IEnumerable<User> GetAll() => ...;
    public void Save(User u) => ...;
    public void Delete(int id) => ...;
}

类前 : 后写接口名实现。

命名约定

C# 接口名以 I 开头:IListIEnumerableIDisposable。来自 Java/Kotlin 的人有点逆反,但跟着约定走——所有 .NET 库都这样。

多接口实现

public class Stream : IDisposable, IAsyncDisposable
{
    public void Dispose() { ... }
    public ValueTask DisposeAsync() { ... }
}

类只能继承一个基类,但可以实现任意多个接口

接口的多态

IRepository<User> repo = new UserRepo();   // 用接口类型变量
repo.Save(user);

// 替换实现
repo = new InMemoryUserRepo();   // 单元测试用 mock 实现

接口是"面向接口编程"和 DI 的核心——业务代码依赖接口、不依赖具体实现。

接口默认方法(C# 8+)

public interface ILogger
{
    void Log(string msg);

    // 默认实现:实现类不重写也能用
    void Info(string msg) => Log($"[INFO] {msg}");
    void Error(string msg) => Log($"[ERROR] {msg}");
}

public class ConsoleLogger : ILogger
{
    public void Log(string msg) => Console.WriteLine(msg);
    // Info / Error 都不写也能用
}

向已发布的接口加方法不破坏已有实现——给库作者大福利。

显式实现

public interface IA { void Do(); }
public interface IB { void Do(); }

public class C : IA, IB
{
    void IA.Do() => Console.WriteLine("IA");
    void IB.Do() => Console.WriteLine("IB");
}

var c = new C();
((IA)c).Do();    // IA
((IB)c).Do();    // IB
// c.Do();        // ❌ 显式实现只能通过接口引用调用

两个接口有同名方法时用显式实现区分。也可以隐藏一个方法只通过接口暴露——public API 简洁。

接口能定义什么

C# 接口可以有:

  • 方法(含 default 实现)
  • 属性(含 default getter/setter)
  • 索引器
  • 事件
  • 静态成员(C# 11+)

不能有:实例字段、构造函数。

静态抽象成员(C# 11+)

public interface IAddable<T> where T : IAddable<T>
{
    static abstract T operator +(T a, T b);
    static abstract T Zero { get; }
}

public struct Vec : IAddable<Vec>
{
    public double X, Y;
    public static Vec operator +(Vec a, Vec b) => new() { X = a.X + b.X, Y = a.Y + b.Y };
    public static Vec Zero => new();
}

public T Sum<T>(IEnumerable<T> items) where T : IAddable<T>
{
    var total = T.Zero;
    foreach (var x in items) total += x;
    return total;
}

让接口约束静态运算符——通用泛型数学的基础。.NET 7 起 INumber<T> 等内置接口让你写真正泛型的数学代码。

常见 BCL 接口

接口 用途
IEnumerable<T> 可遍历(foreach 用)
IList<T> / ICollection<T> 集合操作
IDisposable using 资源释放
IAsyncDisposable await using
IComparable<T> 可比较(Sort 用)
IEquatable<T> 自定义相等
IDictionary<K, V> 键值映射
INotifyPropertyChanged 数据绑定通知

记住这些——日常 C# 离不开。

接口 vs 抽象类

接口 抽象类
多继承 ✅ 多实现 ❌ 单继承
字段 ❌(默认实现可用静态字段)
默认实现 ✅(C# 8+)
状态
构造函数

经验:

  • 纯契约 / 多种实现 → 接口
  • 大量共享逻辑 + 部分扩展点 → 抽象类
  • 不确定 → 接口先(更灵活,将来好改)

标记接口(无成员)

public interface ISerializable { }   // 没方法

仅用作"打标记"。BCL 里 ICloneable 之类——现在不推荐了,用 attribute 更清晰。

SOLID 中的 I

接口隔离原则(Interface Segregation):

// ❌ 一个超大接口
public interface IWorker
{
    void Eat();
    void Sleep();
    void Code();
    void Refactor();
    void DeployToProd();
}

// ✅ 小接口
public interface IEater { void Eat(); }
public interface ISleeper { void Sleep(); }
public interface ICoder { void Code(); }

实现类按需组合接口——比强迫人实现不需要的方法好。

→ 下一篇 record + readonly struct