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
答案 0 :(得分:18)
在C#5规范的第13.4.4至13.4.6节中对此进行了解释。下面引用了相关的部分,但基本上如果你明确声明一个类实现了一个接口,那么再次触发接口映射,所以编译器将那个类作为用来计算每个实现的实现接口成员映射到。
13.4.4界面映射
类或结构必须提供类或结构的基类列表中列出的所有接口成员的实现。在实现类或结构中定位接口成员的实现的过程称为接口映射。
类或结构
C
的接口映射定位C
基类列表中指定的每个接口的每个成员的实现。特定接口成员I.M
的实现,其中I
是声明成员M
的接口,通过检查每个类或结构S
来确定,从C
并重复每个连续的C
基类,直到匹配为止:
- 如果
S
包含与I
和M
匹配的显式接口成员实现的声明,则此成员是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() {...} }
Paint
TextBox
方法隐藏了Paint
中的Control
方法,,但它不会改变Control.Paint
到{{}的映射1}} ,并通过类实例和接口实例调用IControl.Paint
将产生以下影响Paint
...
13.4.6界面重新实现
允许继承接口实现的类通过将其包含在基类列表中来重新实现接口。
接口的重新实现遵循与接口的初始实现完全相同的接口映射规则。因此,继承的接口映射对于为接口的重新实现而建立的接口映射没有任何影响。例如,在声明中
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();
interface IControl { void Paint(); } class Control: IControl { void IControl.Paint() {...} } class MyControl: Control, IControl { public void Paint() {} }
将Control
映射到IControl.Paint
并不会影响Control.IControl.Paint
中重新实施的事实,MyControl
将IControl.Paint
映射到MyControl.Paint
}。
答案 1 :(得分:1)
如果Derived
未实现IBase
并声明new string Name
,则表示Derived.Name
和IBase.Name
在逻辑上不相同。因此,当您访问IBase.Name
时,它会在Base
类中查找,并实施IBase
。如果您删除new string Name
属性,则输出将为Derived
,因为现在Derived.Name
= Base.Name
= IBase.Name
。如果您明确地实施IBase
,则输出将为Derived
,因为现在Derived.Name
= IBase.Name
。如果您将o
投射到Derived
,则输出将为Derived
,因为现在您正在访问Derived.Name
而不是IBase.Name
。