简单的继承不起作用
本文关键字:不起作用 继承 简单 | 更新日期: 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#静态类型提供的类型安全的基础
当您对变量调用方法时,该方法调用在编译时被解析为,并且当n
是Node
类型的变量时,编译器无法解析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
声明为Node
。Node
不包含AddLocation
的定义,因此您的代码不会编译。即使您知道实际的底层类型是SuffixNode
,编译器也必须坚持自己的立场,并就错误向您发出警告。
成像此场景:
class OtherNode : Node
{
}
Node n = new OtherNode();
n.AddLocation(...);
这样行吗?当然不是,但什么规则会在编译时省略该代码并允许您的示例?编译器怎么可能知道你在程序的后期没有交换底层类型?它不能。
你走错了路;如果这个方法应该对Node
的所有子代都可用,那么它至少需要在Node
中声明(它可以是抽象的,以便在派生类中被覆盖)。