简单的继承不起作用

本文关键字:不起作用 继承 简单 | 更新日期: 2023-09-27 18:28:07

我似乎忘记了一些最基本的继承规则,因为我不明白为什么这不起作用。我有一个类SuffixNode,它扩展了Node。

节点:

class Node
{
    public char label;
    public Node parent;
    public Dictionary<char,Node> children;
    public Node(Node NewParent, char NewLabel)
    {
        this.parent = NewParent;
        this.label = NewLabel;
        children=new Dictionary<char,Node>();
    }
}

后缀节点:

class SuffixNode: Node
{
    public Dictionary<String, int> Location=new Dictionary<String, int>();
    public SuffixNode(Node NewParent):base(NewParent, '$')
    {
    }
    public void AddLocation(String loc,int offset)
    {
        this.Location.Add(loc, offset);
    }
}

我试图从SuffixNode类调用主程序中的AddLocation方法,但它给了我一个错误,说(在Node类中)不存在这样的方法:

Node n;
char FirstChar = suffix[0]; //first character of the suffix 
if (suffix == "")
{
     return true;
}
//If the first character of a suffix IS NOT a child of the parent
if (!parent.children.ContainsKey(FirstChar))
{
     if (FirstChar == '$')
     {
          n = new SuffixNode(parent);
          n.AddLocation(document, offset);
     }
     else
     {
          n = new Node(parent, FirstChar); //Create a new node with the first char of the suffix as the label
          parent.children.Add(FirstChar, n); //Add new node to the children collection of the parent
     }
}          

我相信这是一个非常简单的答案,但我不明白为什么这不起作用。不应该

Node n = new SuffixNode(parent)

是否允许我访问SuffixNode方法和变量?

简单的继承不起作用

您已经将n的类型声明为Node,实际上它没有AddLocation方法。您必须将其声明为SuffixNode n = new SuffixNode(parent),才能在其上调用子函数,或者向Node添加一个名为AddLocation的(可能是抽象的)方法。

Node n=new SuffixNode(parent)

这一行告诉编译器n的类型是Node。事实上,它当前有一个分配给它的SuffixNode是无关紧要的。编译器只知道它是一个Node,因此您只能在它上调用Node成员

可能最简单的方法是:

n = new SuffixNode(parent);
((SuffixNode)n).AddLocation(document, offset);

这基本上告诉编译器,这里确实有一个后缀节点。

可能更清楚的是:

SuffixNode sn = new SuffixNode(parent);
sn.AddLocation(document, offset);
n = sn;

如果在Node中定义了一个虚拟方法,并在SuffixNode中覆盖它,则在您的场景中会调用SuffixNode版本。如果像您的情况一样,Node中不存在该方法,则不能以这种方式调用它,因为变量n可能包含任何类型的Node。为了调用该方法,必须首先将其强制转换为SuffixNode。

许多答案都正确地指出,不能对类型为基类的变量调用派生类中定义的方法。没有人注意到这是C#静态类型提供的类型安全的基础

当您对变量调用方法时,该方法调用在编译时被解析为并且当nNode类型的变量时,编译器无法解析n.AddLocation

另一种选择是在运行时解析调用,如果n的引用是Node的某个其他子类的实例,而该子类没有AddLocation方法(或者实际上是Node本身的实例),则可能会导致异常。C#类型的系统被明确地设计来避免这种情况。

考虑:

object o1 = "Hello, World!";
Console.WriteLine(o1.Length); //hypothetically fine
object o2 = Math.PI;
Console.WriteLine(o2.Length); //exception!

C#的原理是快速失败:尽可能在编译时捕获编码错误,因为编译时捕获的错误比运行时捕获的更容易修复。

在不改变对象模型的情况下,简单的解决方案是在相关块中引入一个新变量:

if (FirstChar == '$') 
{ 
    SuffixNode sn = new SuffixNode(parent); 
    sn.AddLocation(document, offset); 
    n = sn;
} 
else 
{ 
    n = new Node(parent, FirstChar); //Create a new node with the first char of the suffix as the label 
    parent.children.Add(FirstChar, n); //Add new node to the children collection of the parent 
} 

您需要将AddLocation方法移动到基类。

Node n = new SuffixNode(parent)不应该允许我访问SuffixNode方法和变量吗?

不,变量的类型定义了您可以访问的方法。

如果您想访问它,您需要将变量的类型更改为SuffixNode

或者,如果更有意义的话,可以将它添加到基类中。

您已将n声明为NodeNode不包含AddLocation的定义,因此您的代码不会编译。即使您知道实际的底层类型是SuffixNode,编译器也必须坚持自己的立场,并就错误向您发出警告。

成像此场景:

class OtherNode : Node
{
}
Node n = new OtherNode();
n.AddLocation(...);

这样行吗?当然不是,但什么规则会在编译时省略该代码并允许您的示例?编译器怎么可能知道你在程序的后期没有交换底层类型?它不能。

你走错了路;如果这个方法应该对Node的所有子代都可用,那么它至少需要在Node中声明(它可以是抽象的,以便在派生类中被覆盖)。