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

C#默认接口方法结合基类的调用异常及相关疑问

C#接口默认实现与继承的常见问题解析

一、中间基类导致子类无法覆盖接口默认实现的原因

先看你的代码示例:

private interface IFunny
{
    void TryToCallMe() { }
}

private class BaseFunny : IFunny;

private class DerivedFunny : BaseFunny
{
    public void TryToCallMe() => throw new InvalidOperationException();
}

[Fact]
public void FunStuff()
{
    ((Action)(() => new DerivedFunny().TryToCallMe())).Should().Throw<InvalidOperationException>();
    ((Action)(() => ((IFunny)new DerivedFunny()).TryToCallMe())).Should().NotThrow<InvalidOperationException>();
}

两个断言都成立的核心逻辑是:

  • 接口的默认实现是绑定到接口类型本身的,并非通过继承传递的虚方法。BaseFunny实现IFunny时,只是隐式复用了接口的默认逻辑,但并没有把TryToCallMe定义为自身的虚方法。
  • DerivedFunny里的TryToCallMe只是子类自己的普通同名方法,和IFunny接口的成员没有重写或实现关系。
  • 因此用DerivedFunny静态类型调用时,执行的是子类的方法;转成IFunny类型调用时,CLR直接调用接口的默认实现,完全不会理会子类的同名方法。

二、子类用显式接口实现报错的原因

当你把DerivedFunny的方法改成显式接口实现:

public void IFunny.TryToCallMe() => throw new InvalidOperationException();

编译器报错“未实现IFunny接口”,是因为显式接口实现要求类必须直接声明实现该接口。虽然DerivedFunny通过继承BaseFunny间接成为IFunny的实现者,但显式实现是类与接口之间的直接契约,不能通过继承的“间接身份”来操作。编译器会认为你没有资格直接实现接口成员,除非你在类声明里明确写: IFunny。

三、重复声明实现接口后生效的原因

当你把DerivedFunny改成:

private class DerivedFunny : BaseFunny, IFunny
{
    public void TryToCallMe() => throw new InvalidOperationException();
}

此时第二个断言会抛出异常,原因是:

  • 显式声明: IFunny后,编译器会把DerivedFunny里的TryToCallMe视为对接口成员的直接实现,而非普通同名方法。
  • 当把实例转成IFunny类型调用时,CLR会优先调用子类的实现,而非接口的默认逻辑。这就达成了你预期的“覆盖默认实现”效果,虽然重复声明接口看似冗余,但这是明确告诉编译器子类要直接对接接口的契约。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:55:06