最近兩天寫單元測試,碰到須要驗證一個樹是不是指望的,可是樹這個結構要驗證起來還真是有點煩。。。ide
個人樹大概是這樣的:單元測試
1 class TreeNode<T> 2 { 3 private static readonly TreeNode<T>[] Empty = new TreeNode<T>[0]; 4 public TreeNode() : this(default(T), Empty) { } 5 public TreeNode(T value, IReadOnlyList<TreeNode<T>> children) 6 { 7 Value = value; 8 Children = children; 9 } 10 public T Value { get; set; } 11 public IReadOnlyList<TreeNode<T>> Children { get; set; } 12 }
爲了判等,這個樹實現了IEquatable<TreeNode<T>>接口:測試
1 public override bool Equals(object obj) 2 { 3 var other = obj as TreeNode<T>; 4 if (other == null) 5 { 6 return false; 7 } 8 return Equals(other); 9 } 10 11 public bool Equals(TreeNode<T> other) 12 { 13 return object.Equals(Value, other.Value) && Children.SequenceEqual(other.Children); 14 } 15 16 public override int GetHashCode() 17 { 18 return Value.GetHashCode() ^ Children.Aggregate(Children.Count, (seed, x) => seed ^ x.GetHashCode()); 19 }
看着還不錯,不過因爲T實際是個複雜類型,每次重寫Equals也是個不輕鬆的事情,並且還要把整個指望的樹給構造出來,呵呵,仍是煩啊。。。this
可是,若是隻須要斷定個把簡單屬性,事情就方便了許多,因此,TreeNode須要一個方法來轉換T的類型:spa
1 public TreeNode<TResult> Select<TResult>(Func<T, TResult> selector) 2 { 3 return new TreeNode<TResult>(selector(Value), (from c in Children select c.Select(selector)).ToList()); 4 }
看起來不錯,這樣就能夠有這樣的code來玩轉tree了:code
1 TreeNode<int> intTree = ... 2 TreeNode<string> stringTree = intTree.Select(i => i.ToString());
等等,咱們能夠把這代碼寫的跟linq:blog
1 TreeNode<int> intTree = ... 2 TreeNode<string> stringTree = from i in intTree 3 select i.ToString();
測試代碼繼續啪啦啪啦的寫,唉,測試失敗了,什麼狀況,仔細一看,哦,tree下面節點的順序錯了,而tree的equals方法要求順序,可是這個測試恰好不要求順序,因而我有了兩個選擇:排序
1. 改寫equals方法 (不想折騰集合操做)接口
2. 讓節點排序 (對測試用例而言,構建一個有順序的樹但是很簡單的事情)get
因此,我須要個OrderBy:
1 public TreeNode<T> OrderBy<TKey>(Func<T, TKey> keySelector) 2 { 3 return new TreeNode<T>(Value, (from c in Children 4 orderby keySelector(c.Value) 5 select c.OrderBy(keySelector)).ToList()); 6 }
這下就能夠隨便折騰這個樹了:
1 TreeNode<int> intTree = ... 2 TreeNode<string> stringTree = from i in intTree 3 let m = i % 3 4 order by m 5 select i.ToString();