在微軟標准的Linq中,並沒有DistinctBy擴展和ForEach擴展,但在平時使用工作中卻又經常需要使用到這 兩個功能,照理來說,微軟在Linq中應該包含這兩個擴展才對,可事實上為什麼並沒有呢?本文我就來說說自 己對這兩個擴展的理解!
關於DistinctBy擴展
顧名思義,DistinctBy擴展就是根據一個鍵值進 行唯一性的篩選,將有重復鍵值的元素剔除,僅保留一個!當然Linq中有Distinct擴展,但其功能簡直是弱爆 了!用過的同志相信都對Distinct擴展吐槽無數遍了吧!如果你要使用Distinct擴展實現DistinctBy擴展的功 能,還要專門定義一個輔助類!Oh,My God!這簡直是對程序員的謀殺!但是等經歷過無數個糾結之後,我突 然發現,微軟在標准的Linq中實際上是包含了DistinctBy擴展的功能的,只不過不那麼直接,看下面的示例代 碼:
class Person { public int Age { get; set; } public string Name { get; set; } public override string ToString() { return string.Format("Age:{0} Name:{1}", Age, Name); } } [STAThread] static void Main() { var persons = new[] { new Person{Age = 10,Name="a"}, new Person{Age = 10,Name="b"}, new Person{Age = 10,Name="c"}, new Person{Age = 20,Name="d"} }; foreach (var p in persons.GroupBy(o=>o.Age).Select(g=>g.First())) { Console.WriteLine(p); } }
運行結果
從結果看,代碼成功完成了根 據鍵值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擴展:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> foreachAction) { foreach (var t in source) { foreachAction(t); } }
我不得不說這是簡直是對Linq的“亵渎”啊,試想,如果別人使用了你的這個ForEach之 後,後續操作豈不是成天方夜譚了嗎?所以說這樣設計ForEach是萬萬不可的!即使勉強要設計,也要設計成 這樣才對啊!
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> foreachAction) { foreach (var t in source) { foreachAction(t); } return source; }
而且在我看來,這樣添加ForEach擴展並不妥當,原因在於:這樣設計的ForEach擴展, 破壞了Linq的延遲操作特性!
所以說,我認為微軟不在Linq中包含ForEach擴展算是一個正確的選擇, 如果要用到ForEach功能,還是老老實實用foreach語句來寫吧!其實也並不復雜!