这个问题已经在这里有一个答案:
- 什么是防止通过基/同级类保护的成员访问的真正原因是什么? 6个回答
想想你有下面的代码:
public abstract class MenuItem
{
protected string m_Title;
protected int m_Level;
protected MenuItem m_ParentItem;
public event ChooseEventHandler m_Click;
protected MenuItem(string i_Title, int i_Level, MenuItem i_ParentItem)
{
m_Title = i_Title;
m_Level = i_Level;
m_ParentItem = i_ParentItem;
}
}
和
public class ContainerItem : MenuItem
{
private List<MenuItem> m_SubMenuItems;
public ContainerItem(string i_Title, int i_Level, MenuItem i_ParentItem)
:base(i_Title, i_Level, i_ParentItem)
{
m_SubMenuItems = new List<MenuItem>();
}
public string GetListOfSubItems()
{
string subItemsListStr = string.Empty;
foreach (MenuItem item in m_SubMenuItems)
{
item.m_Title = "test"; // Cannot access protected member the qualifier
must be of type 'Ex04.Menus.Delegates.ContainerItem'
}
return subItemsListStr;
}
}
我真的不明白这个错误背后的逻辑,是的,我已经阅读: http://blogs.msdn.com/b/ericlippert/archive/2005/11/09/491031.aspx
但我还是看到它根据受保护的访问修饰符的定义完全不合逻辑。 我认为这是应该从它的定义是相同的类访问MenuItem
,并为它的所有派生类! ( ContainerItem
等)
你将如何访问受保护的成员一样m_Title
同时举行的参考MenuItem
(因为多态性设计上的原因)?
为什么会出现这种情况?
不能与被争论的答案为“是因为规范是这么说的 ”:
甲protected
基类的成员是在派生类访问仅如果通过派生类类型发生访问 。
但是,让我们探究幕后此限制。
说明
这里发生的是,埃里克利珀描述你链接到博客文章同样的事情。 您的代码确实相当于这个 :
public abstract class MenuItem
{
protected string m_Title;
}
public class ContainerItem : MenuItem
{
void Foo()
{
var derivedItem = new ContainerItem();
derivedItem.m_Title = "test"; // works fine
var baseItem = (MenuItem)derived;
baseItem.m_Title = "test"; // compiler error!
}
}
这里的问题的事实,这可能发生茎。 就目前而言,请忽略这个例子是使用的方法,而不是一个领域的事实 - 我们会回来的。
public abstract class MenuItem
{
protected void Foo() {}
}
public class SomeTypeOfItem : MenuItem
{
protected override void Foo() {}
}
public class ContainerItem : MenuItem
{
void Bar()
{
var baseItem = (MenuItem)something;
baseItem.Foo(); // #1
}
}
看看线#1:编译器如何知道baseItem
实际上不是SomeTypeOfItem
? 如果是,你当然必须不能访问Foo
! 因此,埃里克介绍,编译器是无法静态地证明访问始终是合法的,因为它必须禁止该代码。
请注意,在某些情况下,例如,如果
baseItem = (MenuItem)new ContainerItem();
甚至
baseItem = (MenuItem)this;
编译器确实有足够的信息来证明访问是合法的,但它仍然不会允许代码进行编译。 我想那是因为编译器团队并不认为实施这种特殊情况的处理程序是值得的麻烦(一个观点对此我同情)。
但是...但是...
这一切都很好的方法(和属性,这是真正的方法) - 有关字段是什么? 那这个呢:
public abstract class MenuItem
{
protected string m_Title;
}
public class SomeTypeOfItem : MenuItem
{
protected new string m_Title;
}
public class ContainerItem : MenuItem
{
void Foo()
{
var baseItem = (MenuItem)something;
baseItem.m_Title = "Should I be allowed to change this?"; // #1
}
}
由于字段不能被覆盖,应该在这里没有歧义和代码应编译并设置MenuItem.m_Title
不论是什么类型something
是。
事实上,我想不出一个技术原因编译器不能做到这一点,但有一个很好的理由在任何情况下:一致性。 埃里克本人可能能够提供更丰富的解释。
所以,我能做些什么?
你将如何访问受保护的成员一样m_Title同时举行(因为多态性设计原因)菜单项的参考?
你根本无法做到这一点; 你将不得不作出成员internal
(或public
)。
protected
并不意味着派生类可以访问它,但是,派生类可以访问它自己的实例的属性。 在你的榜样,您可以访问this.m_Title
因为这属于实例本身,而是你正试图访问另一个实例的保护成员(即实例列表m_SubMenuItems
)。
你需要getter和setter方法来访问你想的方式。
希望这使得它更清晰:
class Foo {
protected int x;
public void setX(int x) {
this.x = x;
}
}
class Bar : Foo {
Foo myFoo = new Foo();
public void someMethod() {
this.x = 5; // valid. You are accessing your own variable
myFoo.x = 5; // invalid. You are attempting to access the protected
// property externally
myFoo.setX(5); // valid. Using a public setter
}
}