将派生类明确标记为实现基类的接口

interface IBase { string Name { get; } } class Base : IBase { public Base() => this.Name = "Base"; public string Name { get; } } class Derived : Base//, IBase { public Derived() => this.Name = "Derived"; public new string Name { get; } } class Program { static void Main(string[] args) { IBase o = new Derived(); Console.WriteLine(o.Name); } } 

在这种情况下,输出将是“Base”。

如果我明确声明Derived实现IBase(实际上已经由基类Base实现并且这样的注释似乎没用),则输出将是“Derived”

 class Derived : Base, IBase { public Derived() => this.Name = "Derived"; public new string Name { get; } } 

这种行为的原因是什么?

VS 15.3.5,C#7

它在C#5规范的第13.4.4至13.4.6节中进行了解释。 下面引用了相关的部分,但基本上如果你明确声明一个类实现了一个接口,那么它会再次触发接口映射,因此编译器将该类作为用于确定每个接口成员映射到哪个实现的类。

13.4.4接口映射

类或结构必须提供类或结构的基类列表中列出的接口的所有成员的实现。 在实现类或结构中定位接口成员的实现的过程称为接口映射。

类或结构C接口映射定位C的基类列表中指定的每个接口的每个成员的实现。 特定接口成员IM ,其中I是声明成员M的接口,通过检查每个类或结构S来确定,从C开始并重复每个连续的C类基类,直到找到匹配为止:

  • 如果S包含与IM匹配的显式接口成员实现的声明,则此成员是IM的实现。
  • 否则,如果S包含与M匹配的非静态公共成员的声明,则此成员是IM的实现。 如果多个成员匹配,则未指定哪个成员是IM的实现。 只有当S是一个构造类型时,才会出现这种情况,其中generics类型中声明的两个成员具有不同的签名,但类型参数使它们的签名相同。

13.4.5接口实现inheritance

类inheritance其基类提供的所有接口实现。 在没有显式重新实现接口的情况下,派生类不能以任何方式改变它从其基类inheritance的接口映射。 例如,在声明中

 interface IControl { void Paint(); } class Control: IControl { public void Paint() {...} } class TextBox: Control { new public void Paint() {...} } 

TextBoxPaint方法隐藏了ControlPaint方法, 但它不会改变Control.PaintIControl.Paint的映射 ,并且通过类实例和接口实例调用Paint将具有以下效果

 Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint(); 

13.4.6接口重新实现

允许inheritance接口实现的类通过将其包含在基类列表中来重新实现接口。

接口的重新实现遵循与接口的初始实现完全相同的接口映射规则。 因此,inheritance的接口映射对于为接口的重新实现而建立的接口映射没有任何影响。 例如,在声明中

 interface IControl { void Paint(); } class Control: IControl { void IControl.Paint() {...} } class MyControl: Control, IControl { public void Paint() {} } 

ControlIControl.Paint映射到Control.IControl.Paint事实不影响MyControl的重新实现, MyControlIControl.Paint映射到MyControl.Paint

如果Derived没有实现IBase并声明new string Name ,那意味着Derived.NameIBase.Name在逻辑上是不一样的。 因此,当您访问IBase.Name ,它会在Base类中查找它,实现IBase 。 如果删除new string Name属性,则输出将为Derived ,因为现在Derived.Name = Base.Name = IBase.Name 。 如果您以明确方式实现IBase ,则输出将为Derived ,因为现在Derived.Name = IBase.Name 。 如果将oDerived ,则输出将为Derived ,因为现在您正在访问Derived.Name而不是IBase.Name