You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#:派生类重写接口默认实现的疑问——为何需重声明接口

问题分析与解答

代码定义

public interface I
{
    void M() => Console.WriteLine("I.M");
}

public class C0 : I
{
    public void M() => Console.WriteLine("C0.M");
}

public class C1 : I
{
}

public class D1a : C1
{
    public void M() => Console.WriteLine("D1a.M");
}

public class D1b : C1, I
{
    public void M() => Console.WriteLine("D1b.M");
}

执行代码

I c0 = new C0();
I c1 = new C1();
I d1a = new D1a();
I d1b = new D1b();
c0.M();
c1.M();
d1a.M();
d1b.M();

输出结果

C0.M
I.M
I.M
D1b.M

疑问与解答

疑问点

理解C0和C1的行为,但疑惑:

  1. 为何D1a中的方法实现未替换接口的默认实现?
  2. 为何给D1b添加看似冗余的接口声明就能解决该问题?
  3. 通常派生类无需声明基类已实现的接口,为何重写接口默认成员实现时需要重新声明?

解答

这本质是C#接口默认方法的绑定规则导致的:

  • 当通过接口类型调用方法时,CLR会优先查找直接实现该接口的类型中与接口成员绑定的实现;如果该类型没有提供自定义实现,才会使用接口的默认方法。

关于D1a的行为

D1a继承自C1,而C1是直接实现I的类型——但C1没有重写M方法,所以C1对应的I.M绑定的是接口默认实现。
D1a中定义的public void M()只是一个普通的类成员方法,并没有和接口I的M建立绑定关系(因为D1a没有显式声明实现I)。所以当用I类型变量调用D1a的M时,CLR会沿用C1继承来的接口默认实现,而非D1a的普通方法。

关于D1b的行为

当D1b显式声明: C1, I时,相当于告诉编译器:该类要重新实现接口I。此时D1b中的public void M()会被编译器关联到接口I的M成员,覆盖掉从C1继承来的接口绑定逻辑。所以通过I类型变量调用时,会执行D1b的M方法。

核心结论

接口默认方法的实现绑定是和直接实现接口的类型强关联的,派生类的普通方法无法自动替换基类继承来的接口默认实现。如果要替换,必须显式声明实现该接口,让编译器将自身方法与接口成员绑定。

内容的提问来源于stack exchange,提问作者Andrew Cooper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:10:54