First look at a simple class people (which will be used as a test example):
1 public abstract class People
2 {
3 public bool IsMale { get; private set; }
4 public abstract IEnumerable<People> Children { get; }
5 }
The People class has a children property that returns all children of that people. The people class can eventually form a people tree through the children attribute.
The common traversal extensions for the tree are as follows:
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 }
Using the People class, write several invocation examples:
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);
Of course, there is another situation, only to obtain the descendants of the people (the descendants of the women are married, excluding their descendants), this situation is slightly more complicated, this article more focused on the idea, no longer to show the example code (which friends to achieve, can be sent in the reply).
Now that it's generic, we'll use it in WinForm as a selector to try it:
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>();
The common approach is certainly not dedicated to elegance, with multiple is/as and Cast, mainly because of inheritance, and the type controlcollection of control.controls attributes is not a generic collection.
The above two examples are similar: the tree structure "root" and "descendants" type the same (or have the same base class), the WinForm of the TreeView is Different: the TreeView (Root) contains multiple TreeNode (descendants), Each TreeNode can also contain multiple TreeNode (descendants), "root" and "descendant" types (and no same base class), as shown in the following figure:
We want to use another extension (to invoke the extension method above):
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 }
The calling code is as follows:
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 );
With these two extensions, it is believed that the majority of the "tree" traversal, in order to facilitate the use of some overloads can be.
In addition, the "tree" traversal has a depth of priority and breadth first, here only mention, will not be one to show examples.