Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot access protected member in base class [duplicate]

Consider you have the following code:

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;
        }
}

and

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;
    }
}
  1. I really do not understand the logic behind this error, and yes I have already read: http://blogs.msdn.com/b/ericlippert/archive/2005/11/09/491031.aspx
    But I still see it totally illogical according to the definition of Protected Access modifier. I see it as should be accessible from the same class where it was defined which is MenuItem and for all its derived classes! (ContainerItem ,etc)

  2. How would you access the protected members like m_Title while holding a reference to MenuItem (because of Polymorphism design reasons)?

like image 579
JavaSa Avatar asked Dec 03 '12 12:12

JavaSa


People also ask

How to access protected base members of objects of Class *B?

Inside Derived::foo (), you can access protected base members of objects of class Derived. However, *b is not of type Derived. Rather, it is of type Base, and so it does not have anything to do with your class. It's a different matter if you take a Derived* as an argument -- then you will indeed have access to protected base members.

Why can't I access a protected member of a class?

Because x is a protected member, you can't access it from another object -- even if that object inherits from that class. You can only access it from the derived object itself. Show activity on this post.

What is the difference between protected and private base class?

In a protected base class, public and protected members of the base class are protected members of the derived class. In a private base class, public and protected members of the base class become private members of the derived class. In all cases, private members of the base class remain private.

Why can't I access *B in derived class?

A common misunderstanding. Inside Derived::foo (), you can access protected base members of objects of class Derived. However, *b is not of type Derived. Rather, it is of type Base, and so it does not have anything to do with your class.


1 Answers

Why does this happen?

An answer that cannot be argued with is "because the spec says so":

A protected member of a base class is accessible in a derived class only if the access occurs through the derived class type.

But let's explore this restriction behind the scenes.

Explanation

What happens here is the same thing that Eric Lippert describes in the blog post that you linked to. Your code does the equivalent of this:

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!
    }
}

The problem here stems from the fact that this might happen. For the moment, please disregard the fact that this example uses a method instead of a field -- we 'll come back to it.

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
    }
}

Look at line #1: how does the compiler know that baseItem is not actually a SomeTypeOfItem? If it is, you certainly must not be able to access Foo! So, as Eric describes, the compiler is unable to statically prove that the access is always legal and because of that it has to disallow this code.

Note that in some cases, for example if

baseItem = (MenuItem)new ContainerItem();

or even

baseItem = (MenuItem)this;

the compiler does have enough information to prove that the access is legal but it still will not allow the code to compile. I imagine that's because the compiler team is not convinced that implementing such special-case handlers is worth the trouble (a point of view which I am sympathetic to).

But... but...

That's all well and good for methods (and properties, which are really methods) -- what about fields? What about 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
    }
}

Since fields cannot be overridden, there should be no ambiguity here and the code should compile and set MenuItem.m_Title irrespective of what the type of something is.

Indeed, I cannot think of a technical reason why the compiler couldn't do this, but there is a good reason in any case: consistency. Eric himself would probably be able to provide a richer explanation.

So what can I do?

How would you access the protected members like m_Title while holding a reference to MenuItem (because of Polymorphism design reasons)?

You simply cannot do that; you would have to make the members internal (or public).

like image 133
Jon Avatar answered Oct 11 '22 16:10

Jon