c#擴充方法奇思妙用進階篇七:“樹”通用遍曆器

來源:互聯網
上載者:User
我的上一篇隨筆《c#擴充方法奇思妙用進階篇六:WinForm 控制項選取器》中給出了一個WinForm的選取器,其實質就是一個“樹”的遍曆器,但這個遍曆局限於WinForm的Control類。在資料結構中,“樹”的遍曆是一個通用演算法,那我們為什麼不做一個通用的“樹”遍曆擴充呢?

 

 先看一個簡單的類People(將作為測試用的例子):

1     public abstract class People
2     {
3         public bool IsMale { get; private set; }
4         public abstract IEnumerable<People> Children { get; }
5     }

 People類有一個Children屬性,返回該People的所有孩子。People類通過Children屬性最終可形成一個People樹。

 

 “樹”的通用遍曆擴充參照如下:

 1     public static IEnumerable<T> GetDescendants<T>(this T root, 
 2         Func<T, IEnumerable<T>> childSelector, Predicate<T> filter)
 3     {
 4         foreach (T t in childSelector(root))
 5         {
 6             if (filter == null || filter(t))
 7                 yield return t;
 8             foreach (T child in GetDescendants((T)t, childSelector, filter))
 9                 yield return child;
10         }
11     }

 使用People類,寫出幾個調用樣本:

1     People people;
2     //
3     //擷取所有子孫
4     var descendants = people.GetDescendants(p => p.Children, null);
5     //擷取所有男性子孫
6     var males = people.GetDescendants(p => p.Children, p => p.IsMale);

 當然,還有另外一種情況,只擷取本族人的子孫(子孫中的女性嫁出,不包括她們的子孫),這種情況稍複雜些,本文更側重想法,不再給出範例程式碼(哪們朋友實現了,可發在回複中)。

 既然是通用的,我們就將它用在WinForm中作為選取器試試吧:

1     //Form1.cs
2     //擷取本表單所有控制項
3     var controls = (this as Control).GetDescendants(c => c.Controls.Cast<Control>(), null);
4     //擷取所有選中的CheckBox
5     var checkBoxes = (this as Control).GetDescendants(
6             c => c.Controls.Cast<Control>(),
7             c => (c is CheckBox) && (c as CheckBox).Checked
8         )
9         .Cast<CheckBox>();

 通用的方法寫起來肯定沒有專用的優雅,用了多處 is/as 和 Cast,主要因為這裡涉及到繼承,而且Control.Controls屬性的類型ControlCollection不是泛型集合。

 

 以上兩個例子比較相似:樹結構中“根”與“子孫”類型相同(或具有相同的基類),WinForm中的TreeView就不同了:TreeView(根)包含多個TreeNode(子孫),每個TreeNode也可包含多個TreeNode(子孫),“根”與“子孫”類型不同(也沒有相同的基類),如:

 

 

 我們要使用另外一個擴充(要調用上面的擴充方法):

 1     public static IEnumerable<T> GetDescendants<TRoot, T>(this TRoot root, 
 2         Func<TRoot, IEnumerable<T>> rootChildSelector,  
 3         Func<T, IEnumerable<T>> childSelector, Predicate<T> filter)
 4     {
 5         foreach (T t in rootChildSelector(root))
 6         {
 7             if (filter == null || filter(t))
 8                 yield return t;
 9             foreach (T child in GetDescendants(t, childSelector, filter))
10                 yield return child;
11         }
12     }

  調用代碼如下:

1     //擷取TreeView中所有以“酒”結尾的樹結點             
2     var treeViewNode = treeView1.GetDescendants(
3         treeView => treeView.Nodes.Cast<TreeNode>(),
4         treeNode => treeNode.Nodes.Cast<TreeNode>(),
5         treeNode => treeNode.Text.EndsWith("酒")
6         );

 

 有了這兩個擴充,相信能滿足大部分“樹”的遍曆,為了使用方便還可以進行一些重載。

 另外,“樹”的遍曆有 深度優先 和 廣度優先,這裡只提一下,就不再一一給出樣本了。

 

 本人系列文章《c#擴充方法奇思妙用》,敬請關注!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.