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.


  • Discourse touched me in a no-no place

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


  • area_pol

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

  • FoxDev

    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 other Node<T>s in it.



  • @Maciejasjmj

    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 other Node<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.


  • FoxDev

    @lucas1 Just wondering, why not use List<TreeNode<T>> and save yourself the hassle of implementing TreeRoot<T>?



  • @Bort They don't share a root but exist in the same system.



  • @RaceProUK

    I have some functions which will recursively look down the tree and will find related items in the other "trees".


  • Winner of the 2016 Presidential Election

    @lucas1 said in C# Generics Help:

    @Maciejasjmj

    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:

    @RaceProUK

    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:

    1. Have easy access to several trees in a collection
    2. 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:

    1. Add a property to each node tracking which tree it belongs to (at some memory expense per node),
    2. Add a smart getter that calculates/finds the parent's identity, and/or
    3. 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. Specifying where 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 against TreeNode<T>, then you have two options:

    1. 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> implements IEnumerable<TreeNode<U>>, unlike in the other examples. To match the other examples, use IEnumerable<U>.

    1. Specify a non-generic interface that the generic TreeNode<T> implements, and specify that for TreeRoot:
    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 in TreeNode<U> is - or even if it is a TreeNode<U> and not something else that implements ITreeNode - so you can't use the type of U in TreeRoot<T>.


    Also, like @Maciejasjmj said below, any/every IEnumerable<T> up there might need to be changed to IEnumerable<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 and TreeNode to enumerate Ts directly, or TreeNode<T>s. Either

    public class TreeXXXX<T> : IEnumerable<T>
    

    or

    public class TreeXXXX<T> : IEnumerable<TreeNode<T>>
    

    That where thing is just wrong unless you plan on inheriting TreeNode<T>.


  • Winner of the 2016 Presidential Election

    @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:

    1. 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 like TreeRoot<T, U>. That's also why substituting an explicit type like string 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 ma

    Guyz, 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 ma

    Guyz, 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.


Log in to reply