【问题标题】:C#: Property overriding by specifying the interface explicitlyC#:通过显式指定接口来覆盖属性
【发布时间】:2011-04-11 22:37:59
【问题描述】:

在尝试从 Collection<T> 类重写 ICollection<T>.IsReadOnly 属性的显式接口实现时,我遇到了一些文档,指出不能重写显式接口成员实现,因为它们不能具有诸如 virtual 或 @ 之类的修饰符987654325@。在MSDN 上,他们甚至通过创建由显式接口成员实现调用的另一个抽象或虚拟成员来指定如何使显式接口成员实现可用于继承。到目前为止没有问题。

但是我想知道:为什么在 C# 中可以通过显式指定接口来覆盖任何显式实现的接口成员?

例如,假设我有一个像这样的简单接口,带有一个属性和方法:

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() 方法现在也调用C 中的重写方法!这是为什么呢?

【问题讨论】:

    标签: c# interface overriding language-features explicit-implementation


    【解决方案1】:

    这与显式接口实现无关;这只是继承和接口映射的一般规则的结果:如果A 类型提供IMyInterface 的隐式实现而不是显式实现,您会看到完全相同相同的结果。

    • 类型B 继承自类型A。没有任何内容被覆盖。
      B 提供自己的 AlwaysFalseIsTrue 成员,但它们实现 IMyInterfaceIMyInterface 的实现由继承自 A 的成员提供:当 B 类型的实例被强制转换为 IMyInterface 时,它的行为方式与 A 类型的实例完全相同,因为 @987654337 @ 提供实现接口的成员。
    • 类型C 继承自类型A。同样,没有任何内容被覆盖。
      C 提供了自己的 AlwaysFalseIsTrue 成员,但这次这些成员确实实现了 IMyInterface:当 C 类型的实例是强制转换为IMyInterface,然后C 的成员提供接口实现,而不是A 的成员。

    因为A 类型明确实现IMyInterface,编译器不会警告BC 的成员隐藏A 的成员;实际上,A 的这些成员由于显式接口实现而已被隐藏。

    如果您将类型 A 更改为隐式而不是显式实现 IMyInterface,则编译器会警告 BC 的成员隐藏而不是覆盖 A 的成员,并且您在BC 中声明这些成员时,最好使用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 接口重新实现

    继承接口的类 允许实施 重新实现接口 将其包含在基类列表中。一种 接口的重新实现 遵循完全相同的界面 映射规则作为初始 一个接口的实现。因此, 继承的接口映射没有 对界面产生任何影响 建立的映射 重新实现接口。

    【讨论】:

    • 所以你说任何人都可以为任何显式接口成员提供新实现是合乎逻辑的,并且最重要的是,原始类将调用该新成员(隐藏原始实现的那个) ) 没有任何覆盖的、虚拟的或抽象的方法?
    • 正是因为你没有覆盖你才看到这种行为:例如,类型B提供了一个new实现IsTrue 覆盖 A 类型的 IsTrue 方法。类型 B 仅从 A 继承其对 IMyInterface 的实现。这是实现IMyInterfaceA.IsTrue 方法,因此当您将B 的实例转换为IMyInterface 时,使用的是A.IsTrue 方法。 [...]
    • Type C 还提供了一个 newIsTrue 实现,它不会覆盖来自 A 类型的 IsTrue 方法。但是输入C 直接实现IMyInterface。根据语言规范中的规则,当您将 C 的实例转换为 IMyInterface 时,使用的是 C.IsTrue 方法。
    • 我同意这有点违反直觉,但这只是语言规则的结果!如果A.Test 方法返回((C)this).AlwaysFalse,那么您应该正确地期望它开始在C 中查找匹配的成员,然后是从最新到最旧的祖先(假设this 实际上是C) .同样,当A.Test 返回((IMyInterface)this).AlwaysFalse 时,它会查找实现IMyInterface.AlwaysFalse 的“最新”成员(这是C 提供的成员,再次假设this 实际上是C)。跨度>
    • 当然我看到了隐藏的逻辑,并转换到调用新成员的接口。但在这种情况下,奇怪的是在一个类中可以有两个(或多个)同一个接口的实现。我认为允许在一个类上多次实现相同的接口是一种语言错误,这会导致这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 2012-06-30
    • 2018-08-18
    • 1970-01-01
    相关资源
    最近更新 更多