C# Generics Help
-
I have a class for tree node structure:
public class TreeNode<T> : IEnumerable<T> { //class implementation }
I have a TreeRoot Class:
public class TreeRoot<T> : IEnumerable<T> where T : TreeNode<T> { //class implementation }
TreeRoot can only be one level deep, but can have many TreeNodes attached to it and each TreeNode has a list of child TreeNode elements.
So it basically like a directory structure.
Now I have everything working except for the TreeRoot class. The way the C# compiler sees:
TreeRoot<T> : IEnumerable<T> where T : TreeNode<T>
Is to double wrap the TreeNode before putting it into the TreeRoot<T> class.
So to instantiate the class is retarded e.g.
TreeRoot<TreeNode<TreeNode<T>>> treeRoot = new TreeRoot<TreeNode<TreeNode<T>>>();
However that is obviously fucking stupid. It should be.
TreeRoot<TreeNode<T>> treeRoot = new TreeRoot<TreeNode<T>>();
If I put in a concrete type e.g. a string it works.
TreeRoot<T> : IEnumerable<T> where T : TreeNode<string>
I think I need another Generic Identifier instead of T, but if I put another letter there like S, V, Y like in the examples on MSDN I have compile errors.
I dunno what I am doing wrong. I know it going to be really simple to fix, but I just couldn't find anything in the Generics docs for C# on MSDN.
-
Why on earth are you putting trees inside trees? That's bizarre. (Trees inside trees inside trees is indeed “fucking stupid”, but trees inside trees is still really strange.) If your types look very complicated, that's because you've made code that is too difficult to understand; it only says what it does, not what it means.
Either make a tree mapping with a custom compound key type, or at least use named classes at each level of the type hierarchy so that it is possible to comprehend the purpose of each level instead of the structure. (Also, consider whether a tree is the right choice in the first place.)
-
@lucas1 said in C# Generics Help:
However that is obviously fucking stupid. It should be.
TreeRoot<T> : IEnumerable<T> where T : TreeNode<T>
This is infinite recursion, because
T = TreeNode<T>
.
Maybe this:TreeRoot< N<T> > : IEnumerable< N<T> > where N<T> : TreeNode<T>
or this
TreeRoot< T > : IEnumerable< TreeNode< T > >
-
Try
TreeRoot<T> : IEnumerable<T> where T : TreeNode<U>
?
-
@RaceProUK I tried that no luck unfortunately.
-
@Adynathos That looks simple enough to just work. I will report back tomorrow.
-
@lucas1 said in C# Generics Help:
I have a TreeRoot Class:
Why? Use a designated TreeNode for a root.
Also, this doesn't make sense:
public class TreeRoot<T> : IEnumerable<T> where T : TreeNode<T>
ITYM
public class TreeRoot<T> : IEnumerable<TreeNode<T>>
But I still don't see why you'd do it this way. The way I always did trees, there's always just a
Node<T>
class with a list of otherNode<T>
s in it.
-
I have multiple "trees" from root that are unrelated, however when the items are serialized to disk it is impossible to tell which tree which items relate to without de-serializing them.
EDIT: better explanation.
-
@Adynathos said in C# Generics Help:
TreeRoot< N<T> > : IEnumerable< N<T> > where N<T> : TreeNode<T>
That can't work as C# does not support higher kinds.
@Maciejasjmj said in C# Generics Help:
But I still don't see why you'd do it this way. The way I always did trees, there's always just a
Node<T>
class with a list of otherNode<T>
s in it.I think you have the better approach. Like this:
data Node a = Leaf a | Branch [Node a]
or, in C#
public interface ITreeNode<T> { } public class LeafNode<T> : ITreeNode<T> { public T Value { get; set; } public LeafNode(T value) { Value = value; } } public class BranchNode<T> : ITreeNode<T> { public IEnumerable<T> Children { get; } public BranchNode(IEnumerable<T> children) { ow my hands hurt
-
@lucas1 said in C# Generics Help:
I have multiple "trees" from root that are unrelated.
If they don't share a root - they are unrelated - then they are just a collection of trees.
-
@lucas1 Just wondering, why not use
List<TreeNode<T>>
and save yourself the hassle of implementingTreeRoot<T>
?
-
@Bort They don't share a root but exist in the same system.
-
I have some functions which will recursively look down the tree and will find related items in the other "trees".
-
@lucas1 said in C# Generics Help:
I have multiple "trees" from root that are unrelated, however when the items are serialized to disk it is impossible to tell which tree which items relate to without de-serializing them.
EDIT: better explanation.
@lucas1 said in C# Generics Help:
I have some functions which will recursively look down the tree and will find related items in the other "trees".
Okay, if I'm understanding your needs properly, you need to:
- Have easy access to several trees in a collection
- Be able to easily discover which tree a node belongs to when serialized.
For (1), is there a reason you can't use a dictionary of trees (to give them friendly names instead of indexes or whatnot), with each key pointing to the root
TreeNode<T>
of a particular tree?
For (2), you could:- Add a property to each node tracking which tree it belongs to (at some memory expense per node),
- Add a smart getter that calculates/finds the parent's identity, and/or
- Do some customization of the serialization to add it when serialized.
To answer the more superficial question, just pass the TreeRoot the same thing you pass TreeNode -
T
. Specifyingwhere T : TreeNode<T>
doesn't actually provide any useful information even if it did what you expected: the only condition you care about is what T is, not what it's encapsulated in - you'll be specifying that in the class's implementation anyway.public class TreeNode<T> : IEnumerable<T> { public T Value { get; set; } public List<TreeNode<T>> Children; // rest of implementation }
public class TreeRoot<T> : IEnumerable<T> { public TreeNode<T> FooRoot; public TreeNode<T> BarRoot; public T DefaultNodeValue { get; set; } // rest of implementation }
If, for some reason, it is important to specify that
TreeRoot
constrain againstTreeNode<T>
, then you have two options:- Specify both type parameters:
public class TreeRoot<T, U> : IEnumerable<T> where T : TreeNode<U> { public T FooRoot; public T BarRoot; public U DefaultNodeValue { get; set; } // rest of implementation }
Note that
TreeRoot<T, U>
implementsIEnumerable<TreeNode<U>>
, unlike in the other examples. To match the other examples, useIEnumerable<U>
.- Specify a non-generic interface that the generic
TreeNode<T>
implements, and specify that forTreeRoot
:
public class ITreeNode : IEnumerable { // Not much going on in here }
public class TreeNode<T> : ITreeNode, IEnumerable<T> { public T Value { get; set; } public List<TreeNode<T>> Children; // rest of implementation }
public class TreeRoot<T> : IEnumerable<T> where T : ITreeNode { public T FooRoot; public T BarRoot; // rest of implementation }
You'll note this last option doesn't specify what the
U
inTreeNode<U>
is - or even if it is aTreeNode<U>
and not something else that implementsITreeNode
- so you can't use the type ofU
inTreeRoot<T>
.
Also, like @Maciejasjmj said below, any/every
IEnumerable<T>
up there might need to be changed toIEnumerable<TreeNode<T>>
depending on what you'll be returning from the enumerator.
-
@lucas1 said in C# Generics Help:
I have multiple "trees" from root that are unrelated, however when the items are serialized to disk it is impossible to tell which tree which items relate to without de-serializing them.
I still don't see why you couldn't model it as each (real) tree actually being a subtree hanging from the master node.
As for serialization, do you want each node to be deserializable together with the information of which tree it belongs to? I'd personally just make it a property of the node - either filled in manually when generating the tree, or with some clever getter actually looking it up.
@Dreikin said in C# Generics Help:
To answer the more superficial question, just pass the TreeRoot the same thing you pass TreeNode - T
Depends if he wants each
TreeRoot
andTreeNode
to enumerateT
s directly, orTreeNode<T>
s. Eitherpublic class TreeXXXX<T> : IEnumerable<T>
or
public class TreeXXXX<T> : IEnumerable<TreeNode<T>>
That
where
thing is just wrong unless you plan on inheritingTreeNode<T>
.
-
@Maciejasjmj said in C# Generics Help:
I'd personally just make it a property of the node - either filled in manually when generating the tree, or with some clever getter actually looking it up.
@Maciejasjmj said in C# Generics Help:
Depends if he wants each TreeRoot and TreeNode to enumerate Ts directly, or TreeNode<T>s.
Incorporated into my post now, thanks. ;)
@Maciejasjmj said in C# Generics Help:
That where thing is just wrong unless you plan on inheriting TreeNode<T>.
Probably so in this case, but I included some means to do it 'properly' if it really is necessary for some reason.
-
@Dreikin said in C# Generics Help:
- Specify both type parameters:
public class TreeRoot<T, U> : IEnumerable<T> where T : TreeNode<U> { public T FooRoot; public T BarRoot; public U DefaultNodeValue { get; set; } // rest of implementation }
Without digging into the architectural analysis of whether doing it that way is really a good idea, that's the answer to the immediate question.
When you put
TreeRoot<T> : IEnumerable<T> where T : TreeNode<U>
, .NET can't figure out your generic definition because you have no way to tell it what type U is. You just have to add the U type to the class definition likeTreeRoot<T, U>
. That's also why substituting an explicit type likestring
for U works.
-
@Maciejasjmj said in C# Generics Help:
I still don't see why you couldn't model it as each (real) tree actually being a subtree hanging from the master node.
As for serialization, do you want each node to be deserializable together with the information of which tree it belongs to? I'd personally just maGuyz, it's dumb and impossible to have this debate on whether the architecture makes sense without him first telling us what he's trying to accomplish, which he hasn't. So just deal with the question as asked.
-
I took @Dreikin second suggestion as that was easiest.
I will post a full solution at some point.
-
This post is deleted!
-
@blakeyrat said in C# Generics Help:
@Maciejasjmj said in C# Generics Help:
I still don't see why you couldn't model it as each (real) tree actually being a subtree hanging from the master node.
As for serialization, do you want each node to be deserializable together with the information of which tree it belongs to? I'd personally just maGuyz, it's dumb and impossible to have this debate on whether the architecture makes sense without him first telling us what he's trying to accomplish, which he hasn't. So just deal with the question as asked.
You know, a visual explanation would help the rest of us trying to understand what in the world is the OP trying to achieve. 20 posts, and I can't get what OP is trying to build with regards to trees.