在微軟標准的Linq中,並沒有DistinctBy擴展和ForEach擴展,但在平時使用工作中卻又經常需要使用到這兩個功能,照理來說,微軟在Linq中應該包含這兩個擴展才對,可事實上為什么並沒有呢?本文我就來說說自己對這兩個擴展的理解!
關於DistinctBy擴展
顧名思義,DistinctBy擴展就是根據一個鍵值進行唯一性的篩選,將有重復鍵值的元素剔除,僅保留一個!當然Linq中有Distinct擴展,但其功能簡直是弱爆了!用過的同志相信都對Distinct擴展吐槽無數遍了吧!如果你要使用Distinct擴展實現DistinctBy擴展的功能,還要專門定義一個輔助類!Oh,My God!這簡直是對程序員的謀殺!但是等經歷過無數個糾結之后,我突然發現,微軟在標准的Linq中實際上是包含了DistinctBy擴展的功能的,只不過不那么直接,看下面的示例代碼:
1 class Person 2 { 3 public int Age { get; set; } 4 public string Name { get; set; } 5 public override string ToString() 6 { 7 return string.Format("Age:{0} Name:{1}", Age, Name); 8 } 9 } 10 [STAThread] 11 static void Main() 12 { 13 var persons = new[] { 14 new Person{Age = 10,Name="a"}, 15 new Person{Age = 10,Name="b"}, 16 new Person{Age = 10,Name="c"}, 17 new Person{Age = 20,Name="d"} 18 }; 19 foreach (var p in persons.GroupBy(o=>o.Age).Select(g=>g.First())) 20 { 21 Console.WriteLine(p); 22 } 23 }
運行結果
從結果看,代碼成功完成了根據鍵值Age進行Distinct的操作。
明眼人可能一眼就看出來了,不錯,就是使用GroupBy、Select和First這三個子操作組合出DistinctBy的功能!只不過實現一個DistinctBy功能需要寫這么做代碼實在是不好,於是可以封裝一下,把GroupBy、Select和First這三個子操作的組合封裝成一個DistinctBy擴展方法,就可以方便的使用了。封裝的代碼 too simple,就不貼上來了!
關於ForEach擴展
為什么微軟不在Linq中添加ForEach擴展,這個問題的討論在網上已經很多了,總結一下主要是有下面幾點原因:
1.Linq中已經有Select擴展了,因此不必在實現一個ForEach,因為在大多數情況下可以用Select來實現ForEach的功能(當然我不建議這樣做,因為Select是延遲操作,如果僅僅實現ForEach功能,有些時候代碼不能夠按照設計意圖正確運行);
2.ForEach破壞了Linq的編程模式,就是說破壞了鏈式編程模式,所謂鏈式編程,大概的意思就是將多個操作通過點號"."鏈接在一起,相信寫過Linq的同志很清楚這句話的含義吧!
我看到網上有人這么來設計ForEach擴展:
1 public static void ForEach<T>(this IEnumerable<T> source, Action<T> foreachAction) 2 { 3 foreach (var t in source) 4 { 5 foreachAction(t); 6 } 7 }
我不得不說這是簡直是對Linq的“褻瀆”啊,試想,如果別人使用了你的這個ForEach之后,后續操作豈不是成天方夜譚了嗎?所以說這樣設計ForEach是萬萬不可的!即使勉強要設計,也要設計成這樣才對啊!
1 public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> foreachAction) 2 { 3 foreach (var t in source) 4 { 5 foreachAction(t); 6 } 7 return source; 8 }
而且在我看來,這樣添加ForEach擴展並不妥當,原因在於:這樣設計的ForEach擴展,破壞了Linq的延遲操作特性!
所以說,我認為微軟不在Linq中包含ForEach擴展算是一個正確的選擇,如果要用到ForEach功能,還是老老實實用foreach語句來寫吧!其實也並不復雜!