Cannot access protected member in base class

15,162

Solution 1

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).

Solution 2

protected does mean that the derived class can access it, however, the derived class can access the property of it's own instance. In your example, you can access this.m_Title since that belongs to the instance itself, but you are attempting to access the protected member of another instance (i.e. the instance in the list m_SubMenuItems).

You need getter and setter methods to access it the way you're trying to.

Hopefully this makes it clearer:

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
    }
}
Share:
15,162
JavaSa
Author by

JavaSa

Updated on June 04, 2022

Comments

  • JavaSa
    JavaSa almost 2 years

    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)?

  • Waihon Yew
    Waihon Yew over 11 years
    This is the same false statement as the other answer. Proof that it's false.
  • Daniel Hilgarth
    Daniel Hilgarth over 11 years
    @Jon: Your proof is not operating with the same conditions as the code of the OP. The difference is: You have instances of Derived, he has instances of Base!
  • Waihon Yew
    Waihon Yew over 11 years
    @DanielHilgarth: Of course it's not the same code. Mine was designed to show that it doesn't matter if you have different instances, what matters is the static type of those instances. The answers here say that what matters is the instances.
  • Daniel Hilgarth
    Daniel Hilgarth over 11 years
    @Jon: Exactly, the static type matters. And that's the point here. He has a list of instances with the static type of Base. And that is the very reason why he can't access the protected member. He could cast it to Derived, but only if it really is an instance of that type - which is unknown.
  • Waihon Yew
    Waihon Yew over 11 years
    @DanielHilgarth: Please see my own answer. I can't explain it any better than that.
  • Daniel Hilgarth
    Daniel Hilgarth over 11 years
    @Jon: Well, I think we both agree that the declared type of the instance is relevant and not the instance in itself. Obviously, we both interpreted "another instance" in different ways. For me this implicitly contained the fact, that the "other instance" was of the static type Base. +1 on your answer.
  • Waihon Yew
    Waihon Yew over 11 years
    @DanielHilgarth: That's a fair assessment, and thanks for your vote.
  • JavaSa
    JavaSa over 11 years
    superb answer! I guess it is more logical to add public property for each protected member, what do you think? Is there a difference between public members vs private or protected members with public properties?
  • Waihon Yew
    Waihon Yew over 11 years
    @JavaSa: I edited the answer because the previous version wasn't quite satisfactory -- please have a look again. If you expose the fields through public properties there is absolutely no reason for the fields to be protected, I would consider that bad design.
  • JavaSa
    JavaSa over 11 years
    Thanks but, I once told that members must not be public, and that they are always should be private. The list I hold is having references to potentially diverse collection of different MenuItems of many kinds, and all I wanted is to be able to get value of their already inherited members from the base class , no matter which concrete type it is. I want to be able to reach their value while looking them in a polymorphic manner (base class view)
  • James
    James over 11 years
    @JavaSa I assume by "members" you mean fields as there are loads of reasons why properties should be public. In your scenario, the Title field is obviously required outside the scope of the class itself therefore it should be a public property. If the actual setting of the data is what you are concerned about then make that private, not the property itself.
  • Waihon Yew
    Waihon Yew over 11 years
    @JavaSa: Yes, "no reason to be protected" means that they should be private. The properties themselves of course will be either public or internal, depends on what makes sense for the design.
  • Krish1992
    Krish1992 over 7 years
    Good answer which clarifies why it doesn't work. I can't understand why they decided to go this route however. It's very common to have a hierarchy where you want protected data in the base which derived types collaborate on, which you don't want to make public.