2009-11-04 77 views
4

我正在創建基於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集合屬性,我正在考慮讓每個節點成爲實際的集合。所以我的樹結構實際上只是一系列的集合。然後,我的抽象節點類將使用泛型上的各種約束來控制它可能具有的子節點的種類。

這是否有意義?我不想這樣做是有原因的嗎?我從未在自己的類中使用過泛型,因此我不確定是否忽視了某些東西。

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 
{ 

} 

回答

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 
    } 
+0

是啊,這就是我,除了可能存在思維是一個等價的「T」部分在一個具體的Node類中明確地將T設置爲其他具體的節點類型 – 2009-11-04 06:23:56

+0

我同意這種「感覺真的很糟糕」,但是有人可以給出一個更確切的理由,爲什麼我會或不想要做這樣的事情? – 2009-11-04 17:30:12

+0

也想想組合設計模式,這是你正在嘗試做的事情(http://www.dofactory.com/patterns/patterncomposite.aspx#_self1) – mike 2009-11-04 23:32:10

0

這讓我想起了我一直在玩,我就扔在這裏的代碼建議你把它與一粒「鹽」:我還沒有完全測試它,對我來說,有一些關於這個代碼非常奇怪的事情(尋找「/ /奇怪:」評論)。這是在VS Studio 2010測試版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]); 

最好,比爾

1

遺憾的是沒有。你必須選擇;你想ConcreteNode的兒童財產是NodeCollection<AbstractNode>NodeCollection<ConcreteNode>

當您考慮向集合中添加節點時會出現問題;如果你有一個ConcreteNodeA,你已經把它作爲AbstractNode。然後,您嘗試撥打

concreteA_As_Abstract.Add(concreteB); 

NodeCollection應允許添加; NodeCollection不會。所以你必須做出選擇。新的C#4協方差/逆變特徵可能對您有所幫助(請參閱Eric Lippert的博客瞭解更多信息),但直到VS2010時纔會出現。

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