Недавно я обнаружил, что метод в производном классе может обращаться только к элементам класса, защищенным базовым классом, через экземпляр производного класса (или одного из его подклассов):
class Base
{
protected virtual void Member() { }
}
class MyDerived : Base
{
// error CS1540
void Test(Base b) { b.Member(); }
// error CS1540
void Test(YourDerived yd) { yd.Member(); }
// OK
void Test(MyDerived md) { md.Member(); }
// OK
void Test(MySuperDerived msd) { msd.Member(); }
}
class MySuperDerived : MyDerived { }
class YourDerived : Base { }
Мне удалось обойти это ограничение, добавив статический метод в базовый класс, так как базовым методам разрешен доступ к Base.Member, а MyDerived может вызвать этот статический метод.
Я все еще не понимаю причину этого ограничения. Я видел пару различных объяснений, но они не могут объяснить, почему MyDerived.Test() по-прежнему разрешено получать доступ к MySuperDerived.Member.
Принципиальное объяснение: "Защищенный" означает, что он доступен только для этого класса и его подклассов. YourDerived может переопределить элемент Member(), создав новый метод, который должен быть доступен только для YourDerived и его подклассов. MyDerived не может вызывать переопределенный yd.Member(), потому что это не подкласс YourDerived, и он не может вызывать b.Member(), потому что b может фактически быть экземпляром YourDerived.
ОК, но тогда почему MyDerived вызывает msd.Member()? MySuperDerived может переопределить Member(), и это переопределение должно быть доступно только для MySuperDerived и его подклассов, правильно?
На самом деле вы не знаете, до какой степени вы вызываете переопределенный элемент или нет. И когда член является полем, его нельзя переопределить в любом случае, но доступ по-прежнему запрещен.
Прагматическое объяснение: Другие классы могут добавлять инварианты, о которых ваш класс не знает, и вы должны использовать их публичный интерфейс, чтобы они могли поддерживать эти инварианты. Если MyDerived может напрямую обращаться к защищенным членам YourDerived, это может нарушить эти инварианты.
Мое же возражение применимо и здесь. MyDerived не знает, какие могут быть добавлены mysuperDerived, которые могут быть добавлены в другой сборке другим автором - так почему MyDerived может напрямую обращаться к своим защищенным членам?
У меня создается впечатление, что это ограничение времени компиляции существует как ошибочная попытка решить проблему, которая действительно может быть решена только во время выполнения. Но, может быть, я что-то упустил. У кого-нибудь есть пример проблемы, которая была бы вызвана тем, что MyDerived мог получить доступ к элементам Base, защищенным с помощью переменной типа YourDerived или Base, но не существует уже при доступе к ним через переменную типа MyDerived или MySuperDerived?
-
UPDATE: Я знаю, что компилятор просто следит за спецификацией языка; то, что я хочу знать, является целью этой части спецификации. Идеальный ответ был бы следующим: "Если MyDerived может называть YourDerived.Member(), то произойдет $NIGHTMARE, но это не может случиться при вызове MySuperDerived.Member(), потому что $ITSALLGOOD."