1

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?

Omar
  • 16,329
  • 10
  • 48
  • 66
Matt
  • 3,820
  • 16
  • 50
  • 73
  • Why are you using `Node` instead of `SuffixNode` for `n`? – Abe Miessler Mar 16 '12 at 18:19
  • My tree will be containing different kinds of nodes, I thought it would be useful later if I want to make use of some polymorphic methods. – Matt Mar 16 '12 at 18:22
  • 3
    As an aside, I note that `if (suffix == "")` will *never execute*, because, if the condition were `true`, the preceding line (`char FirstChar = suffix[0];`) would throw an exception. – phoog Mar 16 '12 at 18:24
  • Related http://stackoverflow.com/questions/9672284/out-of-memory-exception-in-c-sharp? – user7116 Mar 16 '12 at 18:26
  • @phoog I had tried this code with just normal nodes before (no suffix nodes) and had run into that problem, thanks for pointing it out :) – Matt Mar 16 '12 at 18:30
  • @Kentz you're welcome. You've probably figured out that the fix is to move the declaration and assigment of `FirstChar` *after* the `if` statement. – phoog Mar 16 '12 at 18:38

7 Answers7

5

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.

Alexander Corwin
  • 1,097
  • 6
  • 11
  • 1
    He can't declare it as SuffixNode because in the other branch of the if it has a `Node` assigned to it. – Chris Mar 16 '12 at 18:23
  • Ok, creating the abstract method makes the most sense in my case, thanks! – Matt Mar 16 '12 at 18:24
  • It doesn't make sense. Abstract methods can only exist in abstract classes. You are creating a concrete instance of `Node` so you can't put an abstract method on it. You will need to either create a new abstract class or create a new class which can inherit from `Node` and be used where you are currently creating `Node`s. Also it looks to me from your code that AddLocation only makes sense on the suffix class (in that it writes to a private variable only in that class). Do you want an AddLocation on all nodes? does it make sense for the object to have that? – Chris Mar 16 '12 at 18:29
  • Sorry for missing the obvious, been working all day and seem to be tired. You're right Chris, the AddLocation method should only be in SuffixNode. There are enough explanations here to help me through it, thanks again for the help :) – Matt Mar 16 '12 at 18:34
  • 1
    @Chris: he *could* create a virtual method in the base class, though it may not be the best solution. – phoog Mar 16 '12 at 18:35
  • @phoog: true. I was coming from the point of view of what I mentioned after that it didn't look like the base class wanted that functionality at all. I should have been more clear but mainly wanted to answer quickly in case the OP wasted time in trying to pursue that plan. :) – Chris Mar 16 '12 at 18:38
3
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;
Chris
  • 27,210
  • 6
  • 71
  • 92
2

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.

lgaud
  • 2,430
  • 20
  • 30
1

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

Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
  • 1
    Oh, sorry... it's because you're calling Node.AddLocation, which doesn't exist. The Node type only knows about what it contains. But you could unbox Node as SuffixNode and call all the methods contained in Node. Just not the other way around. – Chris Gessler Mar 16 '12 at 18:25
  • 1
    @Kentz because the method is a member of the derived class, but the static type of the variable is the base class. That variable could hold an object of (hypothetical) type `PrefixNode` which might not have an `AddLocation` method. – phoog Mar 16 '12 at 18:25
  • @phoog - Thanks. That's what I meant to say :) – Chris Gessler Mar 16 '12 at 18:27
1

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.

Guvante
  • 18,775
  • 1
  • 33
  • 64
1

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).

Ed S.
  • 122,712
  • 22
  • 185
  • 265
1

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 
} 
phoog
  • 42,068
  • 6
  • 79
  • 117
  • Thanks for all the help phoog! Accepted this answer because you not only provide a solution but also explain *why* it wasn't working in the best way. Cheers! – Matt Mar 16 '12 at 18:44