Matt
Matt

Reputation: 3970

Simple inheritance not working?

I seem to have forgotten some of the most basic rules of inheritance because I can't figure out why this won't work. I have a class SuffixNode which extends Node.

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>();
    }
}

SuffixNode:

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);
    }
}

I'm trying to call the AddLocation method in the main program from the SuffixNode class but it gives me an error saying that no such method exists (in Node class):

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
     }
}          

I'm sure it's an extremely simple answer, but I just can't realise why this isn't working. Shouldn't

Node n = new SuffixNode(parent)

allow me to access the SuffixNode methods and variables?

Upvotes: 1

Views: 1459

Answers (7)

phoog
phoog

Reputation: 43056

Many answers have correctly stated that you cannot call a method defined in a derived class on a variable whose type is the base class. None has noted that this is fundamental to the type safety provided by C#'s static typing.

When you call a method on a variable, that method call is resolved at compile time, and there's no way the compiler can resolve n.AddLocation when n is a Node-type variable.

The alternative would be to resolve the call at run-time, which could result in an exception if the referent of n is an instance of some other subclass of Node that doesn't have an AddLocation method (or indeed an instance of Node itself). The C# type system is explicitly designed to avoid that situtation.

Consider:

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

object o2 = Math.PI;
Console.WriteLine(o2.Length); //exception!

The philosophy of C# is fail fast: catch coding errors at compile time whenever possible, because errors caught at compile time are much easier to fix than those caught at run time.

The trivial solution, without changing your object model, would be to introduce a new variable inside the relevant block:

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 
} 

Upvotes: 1

Ed Swangren
Ed Swangren

Reputation: 124742

You have declared n as a Node. Node does not contain a definition for AddLocation, so your code will not compile. Even though you know that the actual, underlying type is a SuffixNode, the compiler has to stick to its guns and call you out on the error.

Imaging this scenario:

class OtherNode : Node
{

}

Node n = new OtherNode();
n.AddLocation(...);

Should that work? Certainly not, but what rule would omit that code from compiling and allow your example? How can the compiler possibly know that you didn't swap the underlying type at a later point in the program? It can't.

You're going the wrong way; if this method should be available to all descendants of Node then it needs to at least be declared in Node (it can be abstract as to be overriden in derived classes).

Upvotes: 1

lgaud
lgaud

Reputation: 2479

If you define a virtual method in Node, and override it in SuffixNode, the SuffixNode version will be called in your scenario. If, as in your case, the method doesn't exist in Node, it can't be called in that way, because the variable n could be holding any kind of Node. In order to call the method, you first must cast it to SuffixNode.

Upvotes: 2

Guvante
Guvante

Reputation: 19223

Shouldn't Node n = new SuffixNode(parent) allow me to access the SuffixNode methods and variables?

No, the type of variable defines what methods you have access to.

You will need to change the type of the variable to SuffixNode if you want access to it.

Alternatively you can add it to the base class if that makes more sense.

Upvotes: 1

Alexander Corwin
Alexander Corwin

Reputation: 1167

You've declared the type of n as Node, which in fact does not have an AddLocation method. You have to declare it as SuffixNode n = new SuffixNode(parent) to be able to call the child's function on it, or else add a (perhaps abstract) method to Node called AddLocation.

Upvotes: 5

Chris
Chris

Reputation: 27619

Node n=new SuffixNode(parent)

This line is telling the compiler that n is of type Node. The fact that it currently has a SuffixNode assigned to it is irrelevant. All the compiler knows is its a Node and thus you can only call Node members on it.

Probably the easiest way to get around this is:

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

This basically tells the compiler that you really have a suffix node here.

Possibly more clear would be:

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

Upvotes: 3

Chris Gessler
Chris Gessler

Reputation: 23123

You need to move the AddLocation method to the base class.

Upvotes: 1

Related Questions