Kristen Hammack
Kristen Hammack

Reputation: 399

Inner class generic type same as outer type

I found a question here that almost answers my question, but I still don't fully understand.

Trying to write a Tree data structure, I did this:

public class Tree<T>
{
    public TreeNode<T> root;
    ...

    public class TreeNode<T>
    {
        List<TreeNode<T>> children;
        T data;
        public T Data { get { return data; } }

        public TreeNode<T>(T data)
        {
            this.data = data;
            children = new List<TreeNode<T>>();
        }
        ...
    }
}

And, anyone who's worked with C# generics apparently knows that I got this compiler warning: Type parameter 'T' has the same name as the type parameter from outer type 'Tree<T>'

My intent was to create an inner class that would be forced to use the same type as the outer class, but I now understand that adding a type parameter actually allows the inner class to be more flexible. But, in my case, I want subclasses of Tree<T> to be able to use TreeNode, for example, like this:

public class IntTree : Tree<int>
{
    ...
    private static IntTree fromNode(TreeNode<int> node)
    {
        IntTree t = new IntTree();
        t.root = node;
        return t;
    }
}

(That method allows the subclass to implement ToString() recursively)

So my question is, if I take out the parameterization, like this:

public class Tree<T>
{
    public TreeNode root;
    ...

    public class TreeNode
    {
        List<TreeNode> children;
        T data;
        public T Data { get { return data; } }

        public TreeNode(T data)
        {
            this.data = data;
            children = new List<TreeNode>();
        }
        ...
    }
}

will the resulting subclass be forced to use integers when creating TreeNodes, and therefore never be able to break the intent I had?

Disclaimer: yes, I know I'm probably doing plenty of things wrong here. I'm still learning C#, coming from a mostly Java and Lisp background, with a little bit of plain C. So suggestions and explanations are welcome.

Upvotes: 4

Views: 2295

Answers (2)

Ren&#233; Vogt
Ren&#233; Vogt

Reputation: 43886

Yes, it will be forced to use the same type. Look at the declaration again:

public class Tree<T>
{
    public class TreeNode
    {
        private T Data;
    }
}

So the type of Data is determined when you instantiate a specific Tree:

var tree = new Tree<int>();

This way the type of Data is declared as int and can be no different.

Note that there is no non-generic TreeNode class. There is only a Tree<int>.TreeNode type:

Tree<int> intTree = new Tree<int>(); // add some nodes
Tree<int>.TreeNode intNode = intTree.Nodes[0]; // for example

Tree<string> stringTree = new Tree<int>(); // add some nodes
Tree<string>.TreeNode stringNode = stringTree.Nodes[0]; // for example

// ERROR: this won't compile as the types are incompatible
Tree<string>.TreeNode stringNode2 = intTree.Nodes[0]; 

A Tree<string>.TreeNode is a different type than Tree<int>.TreeNode.

Upvotes: 3

Dmitry Egorov
Dmitry Egorov

Reputation: 9650

The type T declared in the outer class may already be used in all its inner declarations, so you can simply remove the <T> from the inner class:

public class Tree<T>
{
    public TreeNode root;
    //...

    public class TreeNode
    {
        List<TreeNode> children;
        T data;
        public T Data { get { return data; } }

        public TreeNode(T data)
        {
            this.data = data;
            children = new List<TreeNode>();
        }
       //...
    }
}

Upvotes: 1

Related Questions