Подтвердить что ты не робот

С#: переопределение свойств, явно указывая интерфейс

При попытке переопределить реализацию явного интерфейса свойства ICollection<T>.IsReadOnly из класса Collection<T> я столкнулся с некоторыми документами, в которых указано, что явные реализации элементов интерфейса не могут быть переопределены, поскольку они не могут иметь модификаторы, такие как virtual или abstract. На MSDN они даже заходят так далеко, как указывают, как сделать явную реализацию члена интерфейса доступной для наследования, создав еще один абстрактный или виртуальный элемент, который вызванный реализацией явного интерфейса. На данный момент проблем нет.

Но потом я задаюсь вопросом: Почему в С# возможно переопределить любой явно реализованный элемент интерфейса, просто указав интерфейс явно?

Например, предположим, что у меня есть простой интерфейс, подобный этому, со свойством и методом:

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! Почему это?

4b9b3361

Ответ 1

Это не имеет никакого отношения к явной реализации интерфейса; это просто следствие общих правил наследования и сопоставления интерфейсов: вы увидите точно такие же результаты, если тип 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, и что вы должен идеально использовать модификатор new при объявлении этих членов в B и C.

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

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

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

20.4.4 Реализация интерфейса

Класс, наследующий интерфейс осуществление разрешено повторить реализацию интерфейса включая его в списке базового класса. повторная реализация интерфейса следует точно такой же интерфейс правила отображения в качестве начального реализация интерфейса. Таким образом, унаследованное отображение интерфейса не имеет эффект на интерфейсе картирование, установленное для повторная реализация интерфейса.