无法访问基类中的受保护成员

考虑您有以下代码:

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 m_SubMenuItems; public ContainerItem(string i_Title, int i_Level, MenuItem i_ParentItem) :base(i_Title, i_Level, i_ParentItem) { m_SubMenuItems = new List(); } 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; } } 
  1. 我真的不明白这个错误背后的逻辑,是的,我已经读过: http : //blogs.msdn.com/b/ericlippert/archive/2005/11/09/491031.aspx
    但根据Protected Access修饰符的定义,我仍然认为它完全不合逻辑。 我认为它应该可以从定义它的同一个类访问,它是MenuItem及其所有派生类! ( ContainerItem等)

  2. 在保持对MenuItem的引用时,您将如何访问受保护的成员(如m_Title (由于多态设计原因)?

为什么会这样?

一个无法与之争论的答案是“因为规范是这么说的 ”:

仅当通过派生类类型进行访问时,才能在派生类中访问基类的protected成员。

但让我们在幕后探讨这种限制。

说明

这里发生的事情与Eric Lippert在您链接的博客文章中描述的相同。 您的代码与此等效:

 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 ! 因此,正如Eric所描述的那样,编译器无法静态地certificate访问始终是合法的,因此必须禁止此代码。

请注意,在某些情况下,例如,如果

 baseItem = (MenuItem)new ContainerItem(); 

甚至

 baseItem = (MenuItem)this; 

编译器确实有足够的信息来certificate访问是合法的,但它仍然不允许代码编译。 我想这是因为编译器团队不相信实现这样的特殊情况处理程序是值得的(我同情的观点)。

但是……但……

对于方法(和属性,实际上是方法)来说,这一切都很好也很好 – 那些字段呢? 那这个呢:

 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

实际上,我无法想到编译器无法做到这一点的技术原因,但在任何情况下都有充分的理由:一致性。 埃里克本人可能会提供更丰富的解释。

那我该怎么办?

在保持对MenuItem的引用时,您将如何访问受保护的成员(如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 } }