C#中一种通用的树的生成方式

在写程序时,经常要用到树的这种结构,如果是做界面编程,那么TreeView是一个不错的选择,几个设置就能把数据绑定好,但是如果自己写类呢?相对就麻烦一点。

这里讨论一下如何快速建立自己的树型结构,即怎么把建树的方法抽离出来加以复用。

代码的复用,不外乎类,接口,泛型。

先考虑用接口来实现,定义一个ITreeNode 然后每一个要建立树型结构的结点去实现?感觉不大好,因为你要定义比如Parent Children等一系列的东西,很是很麻烦,每一个实现起来也很困难。

那抽像类?抽象类的继承到是方便,但是在实际使用中涉及各种类型转换,代码写起来不爽。

泛型呢?泛型的结构又过于笼统 ,但是可以折衷一下,就是用泛型定义一个结点的类

(小弟写代码方式都相对“妥协”,一位大人说的,各位将就着看哈)

 

 1 namespace Soway.DB.Tree
 2 {
 3     public class  TreeNode<T>
 4     {
 5 
 6         public T Data { getset; }
 7         public TreeNode<T> Parent { getset; }
 8         public List<TreeNode<T>> Children { getset; }
 9     }
10 }

 

结点类定义好了以后,就要去实现一个 TreeFactory ,将建树的通用算法提出来。

 

namespace Soway.DB.Tree
{

    
    
    public class TreeFactory <T>
    { 
public List<TreeNode<T>> CreateTreeByLevel
            (List<T> Items )
        {
            //////
        }
    }
}

这里的我的方法名已经默认为ByLevel ,即按层建立树,这样,新的问题又出现了:

1.怎么保证输入值Items是已经按层遍立建立好的结点?

2.怎么分层?即怎么区分树的父结点,子结点,同级结点之间的关系 ?

这些问题其实都与泛型的具体类型有关,但如果把具体类型约束了,那就违反我们本意了。

走一步算一步,先这样,把树结点之间的关系定义出来,算是一个枚举吧:

 

namespace Soway.DB.Tree
{
    public enum  TeeNodeCompareResult
    {
        /// <summary>
        
/// 树结点
        
/// </summary>
        Parent,
        /// <summary>
        
/// 子结点
        
/// </summary>
        Child,
        /// <summary>
        
/// 下一个同级结点
        
/// </summary>
        NextNode,
        /// <summary>
        
/// 前一个同级结点
        
/// </summary>
        PreNode,
        /// <summary>
        
/// 同一个结点
        
/// </summary>
        EquealNode ,
        /// <summary>
        
/// 下一层的结点
        
/// </summary>
        NexLevelNode

    }
}

 

有了这个关系以后,于是有了进一步的想法,考虑传递给TreeFactory一个委托,可以通过这个来得到两个结点之间比较关系:

 

namespace Soway.DB.Tree
{
    public delegate TeeNodeCompareResult TeeNodeCompare<T>(T child, T parent);
}

这样,我们的TreeFactory里多了一个泛型委托的成员。。。

 

   private TeeNodeCompare<T> compare;

        public TreeFactory(Tree.TeeNodeCompare<T> Compare)
        {
            this.compare = Compare;

        }

 

现在,当这个泛型委托处理好了以后,我们下一步问题也好办了,就是将输入的Items进行按层排序,这时,只要有一个Comparison<T>()的实现 ,我直接调用 List<T>.Sort(Comprarion<T>)即可了

下面给出这个Comparation<T>的实现 :

 

 private int CompareResult(T ob1, T ob2)
        {
            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>
        
/// 按层建立树
        
/// </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 { getset; }
        public string Name { getset; }
        public String Url { getset; }
        public string View { getset; }
        public string Action { getset; }
        public string Text { getset; }
        public string Image { getset; }


        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);//建立树型结构

 

posted @ 2011-12-08 13:32 葛云飞 阅读(...) 评论(...) 编辑 收藏