在尝试覆盖ICollection<T>.IsReadOnly
类的Collection<T>
属性的显式接口实现时,我遇到了一些文档,指出显式接口成员实现无法被覆盖,因为它们不能具有修饰符,例如{ {1}}或virtual
。在MSDN上,他们甚至通过创建另一个由显式接口成员实现调用的抽象或虚拟成员来指定如何使显式接口成员实现可用于继承。到目前为止没问题。
但后来我想知道:为什么C#可以通过明确指定接口 来覆盖任何显式实现的接口成员? < / p>
例如,假设我有一个这样的简单界面,带有属性和方法:
abstract
一个显式实现接口的类public interface IMyInterface
{
bool AlwaysFalse { get; }
bool IsTrue(bool value);
}
,并且有一个方法A
,它调用自己的接口成员实现。
Test()
正如您所看到的,四个成员中没有一个是虚拟的或抽象的,所以当我定义一个类public class A : IMyInterface
{
bool IMyInterface.AlwaysFalse
{ get { return false; } }
bool IMyInterface.IsTrue(bool value)
{ return value; }
public bool Test()
{ return ((IMyInterface)this).AlwaysFalse; }
}
时,这样:
B
然后,您希望将public class B : A
{
public bool AlwaysFalse
{ get { return true; } }
public bool IsTrue(bool value)
{ return !value; }
}
投射到B
的实例表现得像A
。它确实:
A
现在来了。创建一个类A a = new A();
Console.WriteLine(((IMyInterface)a).AlwaysFalse); // False
Console.WriteLine(((IMyInterface)a).IsTrue(false)); // False
Console.WriteLine(a.Test()); // False
A b = new B();
Console.WriteLine(((IMyInterface)b).AlwaysFalse); // False
Console.WriteLine(((IMyInterface)b).IsTrue(false)); // False
Console.WriteLine(b.Test()); // False
,它是C
的精确副本,除了类声明中的一件事:
B
现在public class C : A, IMyInterface
{ /* ... same as B ... */ }
的实例在转换为C
时,其行为与A
不同,但与A
类似:
C
即使A c = new C();
Console.WriteLine(((IMyInterface)c).AlwaysFalse); // True
Console.WriteLine(((IMyInterface)c).IsTrue(false)); // True
Console.WriteLine(c.Test()); // True
方法现在调用Test()
中的重写方法!这是为什么?
答案 0 :(得分:10)
nothing 与显式接口实现有关;它只是继承和接口映射的一般规则的结果:如果类型A
提供IMyInterface
的隐式而非显式的实现,您会看到完全相同的结果
B
继承自A
类型。什么都没有被覆盖
B
提供了自己的AlwaysFalse
和IsTrue
成员,但他们不实施IMyInterface
; IMyInterface
继承的成员提供A
的实现:当B
类型的实例转换为IMyInterface
时,它的行为方式与实例完全相同类型为A
,因为A
正在为实现该接口的成员提供。C
继承自A
类型。再一次,什么都没有被覆盖
C
提供了自己的AlwaysFalse
和IsTrue
成员,但这次成员做实施IMyInterface
:当C
类型的实例时转换为IMyInterface
然后C
的成员提供接口实现,而不是A
的接口实现。因为类型A
明确地实现了IMyInterface
,所以编译器不会警告B
和C
的成员隐藏A
的成员;实际上,由于显式接口实现,A
的成员已经被隐藏。
如果您更改了类型A
以隐式而不是显式地实现IMyInterface
,那么编译器会警告B
和C
的成员隐藏,而不是覆盖, A
的成员,在B
和C
中声明这些成员时,理想情况下应使用new
修饰符。
以下是语言规范中的一些相关位。 (ECMA-334 spec中的第20.4.2和20.4.4节; Microsoft C#4 spec中的第13.4.4和13.4.6节。)
20.4.2界面映射
特定的实施 接口成员
I.M
,其中I
是 成员M
的界面 声明,由...决定 检查每个类或结构S
, 从C
开始并重复 每个连续的基类C
, 直到找到匹配。20.4.4界面重新实施
继承接口的类 允许实施 重新实现接口 将它包含在基类列表中。一个 重新实现一个接口 遵循完全相同的界面 映射规则作为初始 实现接口。从而, 继承的接口映射没有 对界面产生任何影响 建立的映射 重新实现界面。