我发现很难找到一个关于如何实现父子层次结构类的好例子。 我有一个treeView控件,我想将其转换为类层次结构,为每个节点添加额外的数据,并能够使用IEnumerable轻松迭代每个父节点。
public IEnumerable<Node> GetAllChildsFromParent(Node parent)
{
foreach (Node node in parent.NodeChildsCollection)
{
yield return node;
}
}
我已经实现了以下一段代码,但却陷入了困境,并不是真的 我是否在正确的轨道上有线索?我该如何处理完成此事?
public class NodeChildsCollection : IEnumerable<Node>
{
IList<Node> nodeCollection = new List<Node>();
Node parent;
public Node Parent
{
get { return parent; }
set { parent = value; }
}
public NodeChildsCollection()
{
}
public void AddNode(Node parent, Node child)
{
this.parent = parent;
nodeCollection.Add(child);
}
#region IEnumerable<Node> Members
public IEnumerator<Node> GetEnumerator()
{
foreach (Node node in nodeCollection)
{
yield return node;
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
public class Node
{
NodeChildsCollection nodeChildsCollection = new NodeChildsCollection();
public Node Parent
{
get { return nodeChildsCollection.Parent; }
set { nodeChildsCollection.Parent = value; }
}
public void AddChild(Node child)
{
nodeChildsCollection.AddNode(this, child);
}
}
答案 0 :(得分:2)
您将Node的职责与集合的职责混合在一起。了解如何在集合中设置父级?这不是拥有父母的集合;它的节点。
我会像这样构建我的节点:
public class Node
{
public Node Parent {get;set;} // null for roots
public NodeCollection Children {get; private set;}
public Node()
{
Children = new NodeCollection();
Children.ChildAdded += ChildAdded;
Children.ChildRemoved += ChildRemoved;
};
private void ChildAdded(object sender, NodeEvent args)
{
if(args.Child.Parent != null)
throw new ParentNotDeadYetAdoptionException("Child already has parent");
args.Child.Parent = this;
}
private void ChildRemoved(object sender, NodeEvent args)
{
args.Child.Parent = null;
}
}
NodeCollection看起来像
public class NodeCollection : INodeCollection {/*...*/}
和INodeCollection将是:
public interface INodeColleciton : IList<Node>
{
event EventHandler<NodeEvent> ChildAdded;
event EventHandler<NodeEvent> ChildRemoved;
}
集合职责在Node的Child集合属性上。当然,您可以让节点实现INodeCollection,但这是编程品味的问题。我更喜欢拥有子公共财产(它的框架是如何设计的)。
使用此实现,您不需要实现“GetChildren”方法;公共儿童财产为所有人提供。
答案 1 :(得分:1)
我发现this blog article在尝试解决同样的问题时非常有用。
答案 2 :(得分:1)
如果要将树状数据结构的概念与存储的特定数据分开,请将其作为通用容器使其成为通用容器。
此外,如果树具有单个根,则treenode本身是treenode的集合,因此(与任何集合一样)添加项的方法应称为Add
。如果您经常拥有树集合,那么将子集合作为单独的对象才有意义。这发生在Windows UI中的TreeViews中,因为TreeView的根包含多个节点而不是单个根treenode。但是,在像XML或HTML DOM这样的东西中,总有一个根,所以我认为更简单的东西是合适的。
最后,您不需要使用IEnumerable
实现yield return
内容 - 只需转发到标准容器的实现。
public class TreeNode<TValue> : IEnumerable<TreeNode<TValue>>
{
private List<TreeNode<TValue>> _children = new List<TreeNode<TValue>>();
public TreeNode<TValue> Parent { get; private set; }
public void Add(TreeNode<TValue> child)
{
_children.Add(child);
child.Parent = this;
}
public void Remove(TreeNode<TValue> child)
{
_children.Remove(child);
child.Parent = null;
}
public IEnumerator<TreeNode<TValue>> GetEnumerator()
{
return _children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _children.GetEnumerator();
}
}
实际上,您可以实现IList<TreeNode<TValue>>
并将所有方法转发到列表中,并在添加/删除子项时适当操作Parent
属性。