如何正确看待Linq的DistinctBy扩展和ForEach扩展

在微软标准的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的操作。

明眼人可能一眼就看出来了,不错,就是使用GroupBySelectFirst这三个子操作组合出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语句来写吧!其实也并不复杂!

posted @ 2013-07-06 09:31  ILoveSleep  阅读(3070)  评论(9编辑  收藏  举报