如何限制树结构中节点的子节点

时间:2009-11-04 03:01:48

标签: c# generics data-structures tree

我正在创建一个基于 AbstractNode 类的树结构。 AbstractNode 类具有包含其子节点的通用集合属性。请参阅下面的代码示例。

有没有办法,可能使用泛型,我可以限制 AbstractNode 的具体版本只允许一种类型的子节点?请参阅下面的代码,了解 ConcreteNodeA ,其中ChildNodes属性是 ConcreteNodeB 的集合,而不是 AbstractNode 。这当然不能编译,但我想知道是否还有其他方法可以用来产生同样的效果。

当然,所有内容都适用于 ChildNodes 集合属性,该属性始终为 AbstractNode 类型,但我正在尝试将一些逻辑嵌入到我的类中节点应该是其他节点的子节点。另外,当引用 ChildNodes 属性时,如果我不必将集合强制转换为我应该知道的类型的集合,那将是很好的。

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
}

更新

好吧,我想我想出了我想做什么,但我想知道是否有人认为这“感觉不好”或“闻起来很有趣”以及为什么。而不是我的节点有一个ChildNodes集合属性,我正在考虑使每个节点成为实际的集合。所以我的树结构实际上只是一系列集合。然后,我的抽象Node类将使用泛型上的各种约束来控制它可以拥有的子节点的类型。

这有意义吗?有没有理由我不想这样做?我之前从未在我自己的课程中使用过泛型,所以我不确定我是否忽略了某些东西。

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
{

}

4 个答案:

答案 0 :(得分:1)

不幸的是没有。你必须选择;您希望ConcreteNode的儿童属性为NodeCollection<AbstractNode>还是NodeCollection<ConcreteNode>

当您考虑在集合中添加节点时会出现问题;如果你有一个ConcreteNodeA,你投射为AbstractNode该怎么办?然后,你试着打电话

concreteA_As_Abstract.Add(concreteB);

NodeCollection应该允许添加; NodeCollection不会。所以你必须做出选择。

新的C#4协方差/逆变特征可能对您有所帮助(请参阅Eric Lippert的博客了解更多信息),但直到VS2010才会出现这种情况。

答案 1 :(得分:0)

可能像

public abstract class AbstractNode<T> //where T : AbstractNode
{

  public abstract NodeCollection<T> ChildNodes
  {
    get;
    set;
  }
}
可能有用吗?只是不确定已注释掉的部分

编辑:内部感觉非常糟糕,但它会编译......

  public abstract class BaseNode
  {
  }

  public abstract class AbstractNode<T> : BaseNode where T : BaseNode
  {
    public abstract NodeCollection<T> ChildNodes
    {
      get;
      set;
    }
  }

  public class ConcreteNodeA : AbstractNode<ConcreteNodeA>
  {
    public void Special() { }

    public override NodeCollection<ConcreteNodeA> ChildNodes
    {
      get;
      set;
    }
  }

  public class ConcreteNodeB : AbstractNode<ConcreteNodeA>
  {
    public void DoSomething()
    {
      ChildNodes[0].ChildNodes[0].ChildNodes[0].Special();
    }

    public override NodeCollection<ConcreteNodeA> ChildNodes
    {
      get;
      set;
    }
  }

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

答案 2 :(得分:0)

这让我想起了我一直在玩的东西,我会在这里抛出代码,建议你带着“一粒盐”:我还没有完全测试它,对我而言,关于这段代码的一些非常奇怪的事情(寻找“//很奇怪:”评论)。这是在VS Studio 2010 beta 2中完成的,针对FrameWork 4.0编译。

using System;
using System.Collections.Generic;
using System.Linq;

// WARNING : EXPERIMENTAL CODE : DO NOT USE FOR ANYTHING BUT EDUCATIONAL PURPOSES

// comments about how crazy the code is : are welcome :)

namespace stronglyTypedTree
{
    // TreeNodes is a strongly typed List of strongly typed Nodes
    public class TreeNodes<T> : List<Node<T>>
    {
        // weird : sometimes the compiler informs me that new is
        // required, if i have not used new, and sometimes it informs
        // me, when I have added new, that new is not required
        public new void Add(Node<T> newNode)
        {
            Console.WriteLine("Add called in TreeNodes class : Type = " + typeof(T).ToString() + " : Node Key = " + newNode.Key.ToString());
            newNode.Parent = this;
            base.Add(newNode);
        }
    }

    // strongly typed Node
    public class Node<T>
    {
        // note : implement a key/value pair
        // instead of this ?
        internal T _key;

        // experimental : have not fully considered
        // the case of root nodes

        // better to make this a property ?
        public TreeNodes<T> Parent;

        // better to make this a property ?
        public TreeNodes<T> Nodes;

        public Node()
        {
            Nodes = new TreeNodes<T>();
        }

        // weird : calling base() here does NOT seem to call the
        // parameterless ctor above : the Nodes collection is, thus, 
        // not instantiated : will cause errors at run-time !
        public Node(T keyValue) : base()
        {
            _key = keyValue;
            // had to insert this : see note above
            Nodes = new TreeNodes<T>();
        }

        public T Key
        {
            get { return _key; }
            set { _key = value; }
        }
    }

    public class Tree<T>
    {
        public TreeNodes<T> Nodes;

        public string Name;

        public Tree()
        {
            Nodes = new TreeNodes<T>();
        }

        // weird : see note on ctor with one parameter
        // in the Node class above
        public Tree(string treeName) : base()
        {
            Name = treeName;
            // had to insert this : see note above
            Nodes = new TreeNodes<T>();
        }
    }

    // define some strongly typed Node classes

    // weird : i thought i could get away with not defining explicit ctors :
    // that ctor's of the Node class would be automatically invoked
    public class intNode : Node<int>
    {
        public intNode() : base() { }

        public intNode(int keyValue) : base(keyValue) { }
    }

    public class strNode : Node<string>
    {
        public strNode() : base() { }

        public strNode(string keyValue) : base(keyValue) { }
    }
}

一些示例测试电话:

    intNode myIntNode1 = new intNode();
    myIntNode1.Key = 100;
    intNode myIntNode2 = new intNode(777);

    strNode myStrNode1 = new strNode();
    myStrNode1.Key = "hello";
    strNode myStrNode2 = new strNode("string node 2");

    Tree<int> intTree = new Tree<int>();
    intTree.Name = "Tree of Integer";

    Tree<string> strTree = new Tree<string>("Tree of String");

    intTree.Nodes.Add(myIntNode1);
    intTree.Nodes.Add(myIntNode2);

    strTree.Nodes.Add(myStrNode1);
    strTree.Nodes.Add(myStrNode2);

    myIntNode1.Nodes.Add(new intNode(999));
    myStrNode2.Nodes.Add(new strNode("subNode of strNode2"));

    Console.WriteLine(intTree.Nodes.Count);

    Console.WriteLine(intTree.Nodes[0]);

    Console.WriteLine(strTree.Nodes.Count);

    Console.WriteLine(strTree.Nodes[1]);

最好,比尔

答案 3 :(得分:0)

这就是你想要的:

namespace Foo 
{   
    public interface INode
    {
        string Speak();
    }

    public abstract class AbstractRoot<T> where T : INode
    {
        public abstract IList<T> Children { get; set; }
    }

    public class GammaChild : INode
    {
        public string Speak() { return "I am GammaNode."; }
    }

    public class BetaChild : AbstractRoot<BetaChild>, INode
    {
        public string Speak() { return "I am BetaNode."; }
        public string BetaSpeak() { return "I am talking Beta-specific things."; }

        private IList<BetaChild> children;
        public override IList<BetaChild> Children { get { return children; } set { children = value; } }
    }

    public class AlphaRoot<T> : AbstractRoot<T>, INode where T : BetaChild
    {
        public string Speak() { return "I am AlphaRoot."; }

        private IList<T> children;
        public override IList<T> Children { get { return children; } set { children = value; } }
    }

    public class Test
    {
        public void Run()
        {
            AlphaRoot<BetaChild> alphaBetaTree = new AlphaRoot<BetaChild>();
            alphaBetaTree.Children.Add(new BetaChild());

            alphaBetaTree.Children[0].BetaSpeak();

            AlphaRoot<GammaChild> alphaGammaTree = new AlphaRoot<GammaChild>();
            alphaGammaTree.Children.Add(new GammaChild());
        }
    }
}

正如预期的那样,尝试将树用于GammaChild时的编译错误是:

The type 'Foo.GammaChild' must be convertible to 'Foo.BetaChild' in order to use it as parameter 'T' in the generic type or method 'Foo.AlphaRoot<T>'