C#中一种通用的树的生成方式
在写程序时,经常要用到树的这种结构,如果是做界面编程,那么TreeView是一个不错的选择,几个设置就能把数据绑定好,但是如果自己写类呢?相对就麻烦一点。
这里讨论一下如何快速建立自己的树型结构,即怎么把建树的方法抽离出来加以复用。
代码的复用,不外乎类,接口,泛型。
先考虑用接口来实现,定义一个ITreeNode 然后每一个要建立树型结构的结点去实现?感觉不大好,因为你要定义比如Parent Children等一系列的东西,很是很麻烦,每一个实现起来也很困难。
那抽像类?抽象类的继承到是方便,但是在实际使用中涉及各种类型转换,代码写起来不爽。
泛型呢?泛型的结构又过于笼统 ,但是可以折衷一下,就是用泛型定义一个结点的类
(小弟写代码方式都相对“妥协”,一位大人说的,各位将就着看哈)
2 {
3 public class TreeNode<T>
4 {
5
6 public T Data { get; set; }
7 public TreeNode<T> Parent { get; set; }
8 public List<TreeNode<T>> Children { get; set; }
9 }
10 }
结点类定义好了以后,就要去实现一个 TreeFactory ,将建树的通用算法提出来。
{
public class TreeFactory <T>
{
public List<TreeNode<T>> CreateTreeByLevel
(List<T> Items )
{
//////
}
}
}
这里的我的方法名已经默认为ByLevel ,即按层建立树,这样,新的问题又出现了:
1.怎么保证输入值Items是已经按层遍立建立好的结点?
2.怎么分层?即怎么区分树的父结点,子结点,同级结点之间的关系 ?
这些问题其实都与泛型的具体类型有关,但如果把具体类型约束了,那就违反我们本意了。
走一步算一步,先这样,把树结点之间的关系定义出来,算是一个枚举吧:
{
public enum TeeNodeCompareResult
{
/// <summary>
/// 树结点
/// </summary>
Parent,
/// <summary>
/// 子结点
/// </summary>
Child,
/// <summary>
/// 下一个同级结点
/// </summary>
NextNode,
/// <summary>
/// 前一个同级结点
/// </summary>
PreNode,
/// <summary>
/// 同一个结点
/// </summary>
EquealNode ,
/// <summary>
/// 下一层的结点
/// </summary>
NexLevelNode
}
}
有了这个关系以后,于是有了进一步的想法,考虑传递给TreeFactory一个委托,可以通过这个来得到两个结点之间比较关系:
{
public delegate TeeNodeCompareResult TeeNodeCompare<T>(T child, T parent);
}
这样,我们的TreeFactory里多了一个泛型委托的成员。。。
public TreeFactory(Tree.TeeNodeCompare<T> Compare)
{
this.compare = Compare;
}
现在,当这个泛型委托处理好了以后,我们下一步问题也好办了,就是将输入的Items进行按层排序,这时,只要有一个Comparison<T>()的实现 ,我直接调用 List<T>.Sort(Comprarion<T>)即可了
下面给出这个Comparation<T>的实现 :
{
switch (compare(ob1, ob2))
{
case TeeNodeCompareResult.Child:
case TeeNodeCompareResult.NextNode:
case TeeNodeCompareResult.NexLevelNode:
return 1;
case TeeNodeCompareResult.Parent :
case TeeNodeCompareResult.PreNode:
return -1;
default :
return 0;
}
}
好,这些基础工作做完以后,建树的就容易了:
/// 按层建立树
/// </summary>
/// <param name="Items">建立树的集合</param>
/// <returns>建立好的树结构</returns>
public List<TreeNode<T>> CreateTreeByLevel
(List<T> Items )
{
Items.Sort(new Comparison<T>(this.CompareResult));
List<TreeNode<T>> result = new List<TreeNode<T>>();
TreeNode<T> lastNode =null;
Queue<TreeNode<T>> queue = new Queue<TreeNode<T>>();
TreeNode<T> currentNode=null;
var current = result;
if (Items.Count > 0)
{
for (int i = 0; i < Items.Count ; i++)
{
TreeNode<T> AddedNode = new TreeNode<T>(){Data=Items[i],
Parent = null,Children = new List<TreeNode<T>>()};//生成要添加的数据
queue.Enqueue(AddedNode);//入队
//看是否到了下一层的结点
if (lastNode != null &&
(compare(AddedNode.Data, lastNode.Data) == Tree.TeeNodeCompareResult.Child
|| compare(AddedNode.Data, lastNode.Data) == Tree.TeeNodeCompareResult.NexLevelNode)//下一层:即结点是子结点或是下一层结点
)
{
currentNode = queue.Dequeue();
}
//找到对应的父结点
while (currentNode != null
&&
compare(AddedNode.Data, currentNode.Data) != TeeNodeCompareResult.Child
)
{
currentNode = queue.Dequeue();
}
if (currentNode !=null && compare(AddedNode.Data, currentNode.Data) != TeeNodeCompareResult.EquealNode)
{
AddedNode.Parent = currentNode;
current = currentNode.Children;
}
current.Add(AddedNode);
lastNode = AddedNode;
}
}
return result;
}
下面是一个使用的Demo ^_^
{
[Table(Name="Auth")]
public class Auth
{
[Column(IsKey=true)]
public string Code { get; set; }
public string Name { get; set; }
public String Url { get; set; }
public string View { get; set; }
public string Action { get; set; }
public string Text { get; set; }
public string Image { get; set; }
public override string ToString()
{
return Code + " " + Name;
}
}
//比较结点的关系:
static Soway.DB.Tree.TeeNodeCompareResult compare(Auth ob1, Auth ob2)
{
if (ob1.Code == ob2.Code)
return TeeNodeCompareResult.EquealNode;
if (ob1.Code.Length > ob2.Code.Length)
if (ob1.Code.IndexOf(ob2.Code) == 0)
return TeeNodeCompareResult.Child;
else
return TeeNodeCompareResult.NexLevelNode;
else if (ob1.Code.Length < ob2.Code.Length)
return TeeNodeCompareResult.Parent;
else if (ob1.Code.CompareTo(ob2.Code) < 0)
return TeeNodeCompareResult.PreNode;
else
return TeeNodeCompareResult.NextNode;
}
///主函数中
var c = new Soway.DB.DBContext(builder.ToString());
var items = c.Get<Auth >();//初始化
var tree = new TreeFactory<Auth>(new TeeNodeCompare<Auth>(compare)).CreateTreeByLevel(items);//建立树型结构