派生类中无法访问受保护的方法

4
我不明白为什么这段代码会给我一个Intellisense错误。
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();
    }
}

在上面指定的行中,我遇到了错误:
Error CS1540:不能通过类型为“Node”的限定符访问受保护成员“Node.ToText()”;限定符必须是类型为“HtmlNode”(或其派生类)。 HtmlNode 是 Node 的一个派生类,为什么 HtmlNode 不能访问 Node 的受保护成员?
如何修改代码以使用“类型为 HtmlNode 的限定符”,正如错误消息中建议的那样?

问题在于Childnodes列表是抽象节点列表,这没有意义。必须像HTMLNODE类中一样实现抽象Node类。要查看它的工作原理,请将子节点更改为List<Htmlnode>... - JWP
2
@JohnPeters:我正在使用抽象化,所以我认为这是完全有意义的。我可以有几个Node子类,并且应该能够访问所有这些子类共同拥有的方法。你的解决方法只适用于HtmlNode类型的节点。这不是我需要的。 - Jonathan Wood
1
如果 node 不是 HtmlNode,那么它会崩溃。 - Yeldar Kurmangaliyev
我在这里找到了类似的问题: https://dev59.com/5mYr5IYBdhLWcg3wb5rc - Grygier
好的,我之前走错了方向...但实际上抽象方法是受保护的。这意味着为了使用它,类必须能够传递该方法的受保护状态。换句话说,实现抽象的类可以访问它。受保护成员的定义是“受保护成员在其类内部和派生类实例中可访问”。 - JWP
1
@JohnPeters:但是HtmlNode 确实是实现抽象的类。为什么派生类不能访问基类的受保护成员? - Jonathan Wood
2个回答

9
据我所见,您可以通过继承机制访问受保护的成员,但仍无法通过实例访问受保护的成员。
根据MSDN上的说法,关键字protected意味着以下内容:
protected”——访问权限被限制于包含类从包含类派生的类型
现在,让我们想象编译器允许您编译它,并且现在您正在遍历您的集合并调用.ToText()方法。
如果node是一个HtmlNode或它的子类,则您可以调用此方法。
然而,如果node是某种类型的AnotherNode
public class AnotherNode : Node
{
    protected override string ToText()
    {
    }  
}

在这种情况下,您试图调用受保护的AnotherNode类的ToText方法。
您是“包含类(AnotherNode)”吗?不是。
您是“派生自AnotherNode”的类型吗?不是。
因此,看起来您应该无法访问此方法。
由于ChildNodes是在编译时未知类型的实例,编译器无法确定您是否有权访问其方法。
这就是为什么编译器会抛出此异常的原因。

5
我猜这就是答案。当我还在用C++编程时,我记得这很完美。如果在C#中也允许这样做,那将会很有用。 - Jonathan Wood

0
你需要在派生类中提供ToText()方法的实现,但是你在实现中调用了基类的ToText()方法,这是没有意义的。想象一下,如果你尝试这样做,这也不会起作用:
public class HtmlNode : Node
{
    protected override string ToText()
    {
        base.ToText(); // Will not compile: Cannot call abstract base member
    }
}

如果你使用HtmlNode类,事情会变得更加清晰。那么让我们来使用它:

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

HtmlNode 调用 NodeBToText() 方法有意义吗?不,因为 HtmlNode 没有继承 NodeB,所以为什么应该允许它访问 NodeBprotected 方法呢?


3
这就是虚函数的作用,这很有道理。 - Jonathan Wood
@JonathanWood 但它不是虚拟的。它是抽象的。在C#中,它们被区别对待。 - CodingYoshi
2
抽象方法自动成为虚方法。否则,就无法实现它们。 - Jonathan Wood
@JonathanWood 好的,你说得对,抽象方法确实是虚拟的。但是抽象方法必须被实现,在你实现它们的时候,你不能调用它。这没有意义。 - CodingYoshi
但它不是虚拟的,而是抽象的。在C#中,它们被视为不同的东西。这也是完全错误的。当然,抽象方法是虚拟的...唯一的区别是基类中没有实现,因此派生类必须提供一个实现,而不需要覆盖基类中的非抽象虚拟方法。C#抽象方法与C++的“纯虚拟”函数相同,其中的主体写为= 0;。无论如何,这是一个转移话题...如果OP的函数不是抽象的,仍然无法调用它。 - Jim Balter
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接