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 интерфейс по включая его в список базовых классов. Ля повторная реализация интерфейса следует точно такой же интерфейс правила отображения как начальные реализация интерфейса. Таким образом, унаследованное сопоставление интерфейса не имеет не повлияет на взаимодействие картографирование, установленное для повторная реализация интерфейса.