Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I restrict the children of nodes in a tree structure

I'm creating a tree structure that is based on an AbstractNode class. The AbstractNode class has a generic collection property that contain its child nodes. See the code example below.

Is there some way, possibly using generics, that I can restrict a concrete version of AbstractNode to only allow one type of child node? See the code below for ConcreteNodeA, where its ChildNodes property is a collection of ConcreteNodeB rather then AbstractNode. This of course does not compile, but I'm wondering if there is some other method I could use to have the same effect.

Of course everything will work with the the ChildNodes collection property always being of type AbstractNode, but I'm trying to embed some logic into my classes in regards to what nodes should be the children of other nodes. Plus when referencing the ChildNodes property it would be nice if I didn't have to cast the collection into a collection of the type I know it should be.

public abstract class AbstractNode
{

    public abstract NodeCollection<AbstractNode> ChildNodes
    {
        get;
        set;
    }
}

public class ConcreteNodeA : AbstractNode
{
    //THIS DOES NOT COMPLILE
    //Error 1   'ConcreteNodeA.ChildNodes': type must be 'NodeCollection<AbstractNode>' 
    //to match overridden member 'AbstractNode.ChildNodes'  
    public override NodeCollection<ConcreteNodeB> ChildNodes
    {
        get;
        set;
    }
}

public class ConcreteNodeB : AbstractNode
{
    public override NodeCollection<AbstractNode> ChildNodes
    {
        get;
        set;
    }
}

public class NodeCollection<T> : BindingList<T>
{ 
    //add extra events here that notify what nodes were added, removed, or changed
}

Update

Alright I think I figured out what I want to do but I'd like to know if anyone thinks this "feels bad" or "smells funny" and why. Rather then my nodes having a ChildNodes collection property I'm thinking of making each Node the actual collection. So my tree structure is would really just be a series of collections of collections. My abstract Node classes will then use various constraints on the generic to control the kinds of sub nodes it can have.

Does this make sense? Is there a reason I wouldn't want to do this? I've never used generics in one of my own classes before so I'm not sure if I'm overlooking something.

public interface INode
{

}

public abstract class AbsNode<T> : BindingList<T>, INode where T : INode
{

}

public abstract class AbsNodeA<T> : AbsNode<T> where T : AbsSubNodeA
{

}

public abstract class ConcreteNodeA : AbsNodeA<AbsSubNodeA>
{

}

public abstract class AbsSubNodeA : INode
{

}

public class ConcreteSubNodeA :AbsSubNodeA
{

}

public class ConcreteSubNodeB :AbsSubNodeA
{

}
like image 396
Eric Anastas Avatar asked Nov 15 '22 14:11

Eric Anastas


1 Answers

Unfortunately not. You have to choose; do you want the Children property of ConcreteNode to be NodeCollection<AbstractNode> or NodeCollection<ConcreteNode>?

The problem comes when you consider adding a node to your collection; what if you have a ConcreteNodeA, which you've cast as AbstractNode. Then, you try to call

concreteA_As_Abstract.Add(concreteB);

NodeCollection should allow the add; NodeCollection will not. So you have to make a choice.

The new C#4 covariance/contravariance features may help you (see Eric Lippert's blog for more) but they aren't around until VS2010.

like image 108
Steve Cooper Avatar answered Dec 20 '22 22:12

Steve Cooper