我最近发现派生类中的方法只能通过派生类(或其子类之一)的实例访问基类的受保护实例成员:
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的方法访问Base.Member,而MyDerived可以调用该静态方法。
但是,我仍然不明白这种限制的原因。我已经看到了几个不同的解释,但他们无法解释为什么仍然允许MyDerived.Test()访问MySuperDerived.Member。Principled说明:'受保护'意味着它只能被该类及其子类访问。 YourDerived 可以覆盖Member(),创建一个只能由YourDerived及其子类访问的新方法。 MyDerived无法调用重写的yd.Member(),因为它不是YourDerived的子类,也不能调用b.Member(),因为b实际上可能是YourDerived的一个实例。
好的,但是为什么MyDerived可以调用msd.Member()? MySuperDerived可以覆盖Member(),并且只有MySuperDerived及其子类才能访问该覆盖,对吧?
直到运行时才知道您是否正在调用被覆盖的成员。当成员是一个字段时,它无论如何都不能被覆盖,但仍然禁止访问。
实用主义解释:其他类可能会添加您的类不了解的不变量,您必须使用它们的公共接口,以便它们可以维护这些不变量。如果MyDerived可以直接访问YourDerived的受保护成员,则可能会破坏这些不变量。
我同样的反对意见适用于此。 MyDerived不知道MySuperDerived可能添加的不变量 - 它可能由不同的作者在不同的程序集中定义 - 为什么MyDerived可以直接访问其受保护的成员?
我得到的印象是,这种编译时限制是作为一种错误的尝试来解决一个实际上只能在运行时解决的问题。但也许我错过了一些东西。是否有人通过让MyDerived通过类型为YourDerived或Base的变量访问Base的受保护成员而导致问题的示例,但是当通过MyDerived类型的变量访问它们时不已经存在MySuperDerived?
-
更新:我知道编译器只是遵循语言规范;我想知道的是规范那一部分的目的。一个理想的答案是,“如果MyDerived可以调用YourDerived.Member(),$ NIGHTMARE会发生,但是当调用MySuperDerived.Member()因为$ ITSALLGOOD而不会发生。”
答案 0 :(得分:17)
答案 1 :(得分:5)
Eric Lippert在one of his blog posts中解释得很清楚。
答案 2 :(得分:1)
“受保护”意味着成员只能访问定义类和所有子类。
由于MySuperDerived
是MyDerived
的子类,Member
可以访问MyDerived
。可以这样考虑:MySuperDerived
是MyDerived
,因此MyDerived
可以访问其私有和受保护的成员(继承自MyDerived
)。
但是,YourDerived
不是MyDerived
,因此MyDerived
无法访问其私人和受保护成员。
您无法访问Member
实例上的Base
因为Base
可能是YourDerived
,而MyDerived
不是MyDerived
,也不是{{1}}的子类{{1}}。
并且不要使用静态方法来允许访问事物。这就是打败封装的目的,并且是一种很难闻,你没有正确设计的东西。
答案 3 :(得分:1)
您的假设是,由于某种模糊的设计纯度概念,明确禁止此行为。我不为微软工作,但我相信事实要简单得多:它不是禁止,它只是支持,因为这样做很费时间影响相对较小。
很少使用的protected internal
可能会涵盖大多数仅protected
没有完全消除它的情况。
答案 4 :(得分:0)
你似乎完全以错误的方式思考这个问题。
这不是“谁可以称之为什么”,而是什么是可替代的。
MyDerived的子类应始终可替代MyDerived(包括其重写的受保护方法)。 Base的其他子类没有这样的约束,所以你不能用它们代替MyDerived。
答案 5 :(得分:0)
http://msdn.microsoft.com/en-us/library/bcd5672a.aspx
基类的受保护成员是 只能在派生类 中访问 访问通过派生来进行 班级类型。
有“什么?”的文档。题。 现在我希望我知道“为什么?” :)
显然virtual
与此访问限制无关。
如果MyDerived可以调用Base.Member,它实际上可能正在处理YourDerived的实例,并且实际上可能正在调用YourDerived.Member
啊,这是同一个问题:C# protected members accessed via base class variable