程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> c#擴展方法奇思妙用高級篇七:“樹”通用遍歷器

c#擴展方法奇思妙用高級篇七:“樹”通用遍歷器

編輯:關於C#

先看一個簡單的類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 );

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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved