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

yev*_*ijz 20 c# inheritance interface

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,输出将是"Base".

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

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

这种行为的原因是什么?

VS 15.3.5,C#7

Jon*_*eet 18

它在C#5规范的第13.4.4至13.4.6节中进行了解释.相关部分被引用如下,但基本上如果你明确声明一个类实现一个接口,这再次触发接口映射,所以编译器采用的是类作为一个用来计算出哪个实现每个接口成员被映射到.

13.4.4接口映射

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

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

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

...

13.4.5接口实现继承

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

interface IControl
{
    void Paint();
}
class Control: IControl
{
    public void Paint() {...}
}
class TextBox: Control
{
    new public void Paint() {...}
}
Run Code Online (Sandbox Code Playgroud)

Paint在方法TextBox隐藏了Paint在方法Control,但它并没有改变的映射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();
Run Code Online (Sandbox Code Playgroud)

...

13.4.6接口重新实现

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

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

interface IControl
{
    void Paint();
}
class Control: IControl
{
    void IControl.Paint() {...}
}
class MyControl: Control, IControl
{
    public void Paint() {}
}
Run Code Online (Sandbox Code Playgroud)

这一事实Control映射IControl.PaintControl.IControl.Paint不影响在重新实现MyControl,它映射IControl.PaintMyControl.Paint.