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

Jav*_*aSa 7 c# inheritance protected

考虑您有以下代码:

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;
        }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 我真的不明白这个错误背后的逻辑,是的,我已经读过:http: //blogs.msdn.com/b/ericlippert/archive/2005/11/09/491031.aspx
    但是我仍然觉得它完全不合逻辑根据Protected Access修饰符的定义.我认为应该可以从定义它的同一个类中访问它,MenuItem以及它的所有派生类!(ContainerItem等)

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

Jon*_*Jon 19

为什么会这样?

一个无法与之争论的答案是"因为规范如此说明 ":

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!
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的问题源于这可能发生的事实.目前,请忽略这个示例使用方法而不是字段的事实 - 我们将回到它.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

看看第1行:编译器如何知道baseItem实际上并不是一个SomeTypeOfItem?如果是,您肯定无法访问Foo!因此,正如Eric所描述的那样,编译器无法静态证明访问始终是合法的,因此必须禁止此代码.

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

baseItem = (MenuItem)new ContainerItem();
Run Code Online (Sandbox Code Playgroud)

甚至

baseItem = (MenuItem)this;
Run Code Online (Sandbox Code Playgroud)

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

但是......但......

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

由于字段不能被覆盖,因此这里不应该有歧义,代码应该编译和设置,MenuItem.m_Title而不管其类型something是什么.

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

那我该怎么办?

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

你根本做不到; 你必须成为成员internal(或public).