4

I don't understand why this code gives me an Intellisense error.

public abstract class Node
{
    protected abstract string ToText();
}

public class HtmlNode : Node
{
    public List<Node> ChildNodes { get; set; }

    protected override string ToText()
    {
        StringBuilder builder = new StringBuilder();

        foreach (var node in ChildNodes)
            builder.Append(node.ToText());  // <=== THIS IS THE ERROR

        return builder.ToString();
    }
}

On the line indicated above, I get the error:

Error CS1540: Cannot access protected member 'Node.ToText()' via a qualifier of type 'Node'; the qualifier must be of type 'HtmlNode' (or derived from it)

HtmlNode derives from Node, so why can't HtmlNode access protected members of Node?

And how would I modify the code to use "a qualifier of type HtmlNode", as suggested in the error message?

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • The problem is that the list of Childnodes is a list of abstract node. That doesn't make sense. The abstract Node class must be implemented as in your HTMLNODE class. To see it work change the child nodes to List... – JWP Jul 11 '17 at 03:54
  • 2
    @JohnPeters: I'm using abstraction, so I would argue that it makes perfect sense. I could have several subclasses of `Node` and should be able to access this method that is common to all of them. Your workaround would only work with nodes of type `HtmlNode`. That's not what I need. – Jonathan Wood Jul 11 '17 at 03:56
  • 1
    @AzarShaikh If `node` is not `HtmlNode`, then it will crash. – Yeldar Kurmangaliyev Jul 11 '17 at 04:00
  • I found similar question asked here: https://stackoverflow.com/questions/13683324/cannot-access-protected-member-in-base-class – Grygier Jul 11 '17 at 04:03
  • Ok I was on wrong track... but indeed the abstract method is protected. Which means in order to use it the class must be able to pass the protected status of the method. In other words the class implementing the abstraction can access it. the definition of protected is "A protected member is accessible within its class and by derived class instances. " – JWP Jul 11 '17 at 04:03
  • 1
    @JohnPeters: But `HtmlNode` *is* the class that implements the abstraction. Why can't a derived class access protected members of the base class? – Jonathan Wood Jul 11 '17 at 04:06

2 Answers2

9

As I see it, you can access protected members through inheritance mechanism, but you still cannot access protected members through instance.

According to MSDN, protected keyword means the following:

protected - Access is limited to the containing class or types derived from the containing class.

Now, let's imagine compiler lets you compile it and now you are iterating through your collection and call .ToText() method.

If node is an HtmlNode or its subclass, then you are able to call this method.

However, if node is some sort of type AnotherNode:

public class AnotherNode : Node
{
    protected override string ToText()
    {
    }  
}

In this case, you are trying to call protected ToText method of AnotherNode class.
Are you a "containing class (AnotherNode)"? No.
Are you a "type derived from AnotherNode"? No.
So, it looks like you should not be able to access this method.

Since ChildNodes are instances of types which are not known at compile time, compiler cannot determine if you should have access to its method or not.
That's how I see why compiler throws this exception.

Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101
  • 5
    I guess this is the answer. Back when I programmed in C++, I recall this being perfectly fine. And it would be useful if it was allowed in C#. – Jonathan Wood Jul 11 '17 at 04:12
0

You need to provide implementation for the ToText() method in the derived class, but you are calling the base class ToText() method within the implementation and that does not make sense. Imagine you tried this, this will not work either:

public class HtmlNode : Node
{
    protected override string ToText()
    {
        base.ToText(); // Will not compile: Cannot call abstract base member
    }
}

Things become more clear if you use the HtmlNode class. So let's use it:

HtmlNode node = new HtmlNode();
node.ChildNodes.Add(new HtmlNode());
node.ChildNodes.Add(new NodeB()); // Say NodeB inherits Node

Does it make sense for HtmlNode to call ToText() method of NodeB? No it does not because HtmlNode does not inherit NodeB so why should it be allowed to access the protected methods of NodeB?

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
  • 3
    That's what virtual functions are for. It makes perfect sense. – Jonathan Wood Jul 11 '17 at 04:26
  • @JonathanWood But it's not virtual. It is abstract. In C# they are treated differently. – CodingYoshi Jul 11 '17 at 04:30
  • 2
    Abstract methods are automatically virtual. Otherwise, there would be no way to implement them. – Jonathan Wood Jul 11 '17 at 04:34
  • @JonathanWood fair enough and yes you are correct that abstract methods are virtual. But abstract methods must be implemented and while you are implementing them, you cannot call it. That makes no sense. – CodingYoshi Jul 11 '17 at 04:40
  • @JonathanWood CodingYoshi writes: "base.ToText(); // Will not compile: Cannot call abstract base member" -- this is a totally different issue, a *compile-time* reference to an undefined function. Inheritance is way different. The compiler *could* accept the OP's code and it would run and work fine, but it's a permissions issue, as explained by Yeldar Kurmangaliyev and at length by Eric Lippert in the link in ashin's deleted (dunno why) answer: https://blogs.msdn.microsoft.com/ericlippert/2010/01/14/why-cant-i-access-a-protected-member-from-a-derived-class-part-six/ – Jim Balter Nov 13 '18 at 01:20
  • " But it's not virtual. It is abstract. In C# they are treated differently. " -- This too is wildly off base. Of course abstract methods are virtual ... the only difference is that there is no implementation in the base class, so derived classes *must* provide one, whereas they need not override a non-abstract virtual method in a base class. C# abstract methods are the same as C++'s "pure virtual" functions, where the body is written as ` = 0;` Anyway, this is a red herring ... nothing would be different here if the OP's function wasn't abstract; he still couldn't call it. – Jim Balter Nov 13 '18 at 01:31
  • P.S. If the method were declared public rather than protected then it could be called, regardless of whether it's abstract, so *But abstract methods must be implemented and while you are implementing them, you cannot call it. That makes no sense* is in fact nonsensical, and shows confusion between compile time and run time. At run time it's always safe to call a virtual method since the vtable of the instance is guaranteed to point to some implementation, since the compiler requires that abstract methods be implemented (so that `= 0` in the base class vtable is never referenced). – Jim Balter Nov 13 '18 at 02:52