C#: переопределение свойства путем явного указания интерфейса

при попытке переопределить явную реализацию интерфейса ICollection<T>.IsReadOnly собственность от Collection<T> class, я наткнулся на некоторые документы, заявляющие, что явные реализации членов интерфейса не могут быть переопределены, потому что они не могут иметь модификаторы, такие как virtual или abstract. On 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! Почему так?

1 ответов


это ничего чтобы сделать с явной реализацией интерфейса; это просто следствие общих правил наследования и отображения интерфейса: вы увидите ровно те же результаты, если тип A при условии неявной, а не явной реализации IMyInterface.

  • тип B наследует от типа A. Ничто не отменяется.
    B предоставляет свой собственный AlwaysFalse и IsTrue членов, но они не реализовать IMyInterface; реализации IMyInterface предоставляется членами, унаследованными от A: когда экземпляр типа B превращается в IMyInterface тогда он ведет себя точно так же, как экземпляр типа A, потому что A предоставляет члены, которые реализуют интерфейс.
  • тип C наследует от типа A. Опять же, ничто не отменяется.
    C предоставляет свой собственный AlwaysFalse и IsTrue участники, но на этот раз эти члены do реализовать IMyInterface: когда экземпляр типа C превращается в IMyInterface члены C обеспечить реализацию интерфейса, а не A.

потому что типа A осуществляет IMyInterface явно компилятор не предупреждает, что члены B и C скрываются члены A; в действительности те члены A уже были скрыты из-за явного интерфейса реализация.

если вы изменили тип A для реализации IMyInterface неявно, а не явно, тогда компилятор предупредил бы, что члены B и C скрываются, не переопределяя, члены A и что вы должны идеально использовать new модификаторов при объявлении этих членов в B и C.

вот некоторые соответствующие биты из спецификации языка. (Раздел 20.4.2 и 20.4.4 в ECMA-334 spec; разделы 13.4.4 и 13.4.6 в Microsoft C#4 spec.)

20.4.2 отображение интерфейса

реализация конкретного член интерфейс I.M, где I это интерфейс, в котором объявляется, определяется изучение каждого класса или структуры S, начиная с C и повторял каждый последующий базовый класс C, пока не будет совпадения.

20.4.4 повторная реализация интерфейса

класс, наследующий интерфейс реализация разрешено re-implement интерфейс по включая его в список базовых классов. Ля повторная реализация интерфейса следует точно такой же интерфейс правила отображения как начальные реализация интерфейса. Таким образом, унаследованное сопоставление интерфейса не имеет не повлияет на взаимодействие картографирование, установленное для повторная реализация интерфейса.