C# 显式地将派生类标记为基类的实现接口
在这种情况下,输出将是“基本” 如果我显式地声明派生实现IBase(实际上已经由基类base实现,并且这样的注释似乎是无用的),那么输出将是“派生的” 这种行为的原因是什么C# 显式地将派生类标记为基类的实现接口,c#,inheritance,interface,C#,Inheritance,Interface,在这种情况下,输出将是“基本” 如果我显式地声明派生实现IBase(实际上已经由基类base实现,并且这样的注释似乎是无用的),那么输出将是“派生的” 这种行为的原因是什么 第15.3.5节,C#7,C#5规范第13.4.4节至第13.4.6节对此进行了解释。下面引用了相关部分,但基本上如果您明确声明一个类实现了一个接口,则会再次触发接口映射,因此编译器会将该类作为一个类,用于计算每个接口成员映射到哪个实现 13.4.4接口映射 类或结构必须提供类或结构的基类列表中列出的所有接口成员的实现。在实
第15.3.5节,C#7,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
- 否则,如果
包含与M匹配的非静态公共成员声明,则此成员是S
的实现。如果有多个成员匹配,则未指定哪个成员是I.M
的实现。只有当I.M
是一个构造类型,其中泛型类型中声明的两个成员具有不同的签名,但类型参数使其签名相同时,才会出现这种情况S
class Derived : Base, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
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();
文本框中的Paint
方法隐藏了Control
中的Paint
方法,但它不会改变控件的映射。Paint
到IControl上。Paint
,通过类实例和接口实例调用Paint
,将产生以下效果
interface IControl
{
void Paint();
}
class Control: IControl
{
public void Paint() {...}
}
class TextBox: Control
{
new public void Paint() {...}
}
13.4.6接口重新实现
允许继承接口实现的类通过将接口包含在基类列表中来重新实现接口
接口的重新实现遵循与接口的初始实现完全相同的接口映射规则。因此,继承的接口映射对为重新实现接口而建立的接口映射没有任何影响。例如,在声明中
class Derived : Base, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
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();
Control
将IControl.Paint
映射到Control.IControl.Paint
的事实并不影响MyControl
中的重新实现,后者将IControl.Paint
映射到MyControl.Paint
如果Derived
未实现IBase
并声明新字符串名称
,则意味着Derived.Name
和IBase.Name
在逻辑上不相同。因此,当您访问IBase.Name
时,它会在Base
类中查找它,实现IBase
。如果删除new string Name
属性,则输出将是Derived
,因为现在Derived.Name
=Base.Name
=IBase.Name
。如果不恰当地实现了IBase
,输出将是派生的
,因为现在派生的.Name
=IBase.Name
。如果将o
强制转换为Derived
,则输出将是Derived
,因为现在您正在访问Derived.Name
,而不是IBase.Name
,为什么它会有其他行为?你的期望是什么?期望-相同的输出,相同的成员访问。我不明白为什么在基类已经实现了这样的接口的情况下向类定义添加接口会改变事情。你知道public new string Name{get;}
对base
上的Name
做了什么吗?嵌套类初始化mattersInIBase o=new-Derived()代码>编译器有两种选择,它选择最佳匹配。你的介绍比引文更具解释性。但有一个问题,当你说再次触发接口映射的时,又是什么意思?接口是按什么顺序完成的,而不是从子接口到父接口,就像解释器的顺序一样?这意味着第一个接口映射只是“赢”了吗?感谢您提供这样的扩展答案。令人困惑的是,只需在接口到类定义中混入接口,就可以在两个类(基类、派生类)中的任何一个类中重新实现接口,而无需显式(语法)实现接口。@nl-x:我的意思是在编译Base
时执行接口映射,然后在编译派生类
时再次执行接口映射。是一个编译时选项,因此它没有执行时间顺序。。。我不知道你在问什么。我不应该提到(运行时)ex