.NET 3.5扩展方法和Lambda表达式
创始人
2024-04-27 09:10:35
0次

对于上文的简化需求,使用Lambda表达式和内置的.NET 3.5扩展方法便可以写成这样:

  1. static List< int> EvenSquareLambda(IEnumerable< int> source)  
  2. {  
  3.     return source.Where(i => i % 2 == 0).Select(i => i * i).ToList();  
  4. }  

.NET 3.5扩展方法的延迟效果

应该已经有许多朋友了解了.NET 3.5中处理集合时扩展方法具有“延迟”的效果,也就是说Where和Select中的委托(两个Lambda表达式)只有在调用ToList方法的时候才会执行。这是优点也是陷阱,在使用这些方法的时候我们还是需要了解这些方法的效果如何。不过这些方法其实都没有任何任何“取巧”之处,换句话说,它们的行为和我们正常思维的结果是一致的。如果您想得明白,能够自己写出类似的方法,或者能够“自圆其说”,十有八九也不会有什么偏差。但是如果您想不明白它们是如何构造的,还是通过实验来确定一下吧。实验的方式其实很简单,只要像我们之前验证“重复计算”陷阱那种方法就可以了,也就是观察委托的执行时机和顺序进行判断。

好,回到我们现在的问题。我们知道了“延迟”效果,我们知道了Where和Select会在ToList的时候才会进行处理。不过,它们的处理方式是什么样的,是像我们的“普通方法”那样“创建临时容器(如List< T>),并填充返回”吗?对于这点我们不多作分析,还是通过“观察委托执行的时机和顺序”来寻找答案。使用这种方式的关键,便是在委托执行时打印出一些信息。为此,我们需要这样一个Wrap方法(您自己做试验时也可以使用这个方法):

  1. static Func< T, TResult> Wrap< T, TResult>(  
  2.     Func< T, TResult> func,  
  3.     string messgaeFormat)  
  4. {  
  5.     return i =>  
  6.     {  
  7.         var result = func(i);  
  8.         Console.WriteLine(messgaeFormat, i, result);  
  9.         return result;  
  10.     };  
  11. }  

Wrap方法的目的是将一个Func< T, TResult>委托对象进行封装,并返回一个类型相同的委托对象。每次执行封装后的委托时,都会执行我们提供的委托对象,并根据我们传递的messageFormat格式化输出。例如:

  1. var wrapper = Wrap< int, int>(i => i + 1, "{0} + 1 = {1}");  
  2. for (var i = 0; i <  3; i++) wrapper(i);  

则会输出:

  1. 0 + 1 = 1 
  2. 1 + 1 = 2 
  3. 2 + 1 = 3 

那么,我们下面这段代码会打印出什么内容呢?

  1. List< int> source = new List< int>();  
  2. for (var i = 0; i <  10; i++) source.Add(i);  
  3.  
  4. var finalSource = source  
  5.     .Where(Wrap< int, bool>(i => i % 3 == 0, "{0} can be divided by 3? {1}"))  
  6.     .Select(Wrap< int, int>(i => i * i, "The square of {0} equals {1}."))  
  7.     .Where(Wrap< int, bool>(i => i % 2 == 0, "The result {0} can be devided by 2? {1}"));  
  8.  
  9. Console.WriteLine("===== Start =====");  
  10. foreach (var item in finalSource)  
  11. {  
  12.     Console.WriteLine("===== Print {0} =====", item);  
  13. }  
  14.  

我们准备一个列表,其中包含0到9共十个元素,并将其进行Where…Select…Where的处理,您可以猜出经过foreach之后屏幕上的内容吗?

  1. ===== Start =====  
  2. 0 can be divided by 3? True  
  3. The square of 0 equals 0.  
  4. The result 0 can be devided by 2? True  
  5. ===== Print 0 =====  
  6. 1 can be divided by 3? False  
  7. 2 can be divided by 3? False  
  8. 3 can be divided by 3? True  
  9. The square of 3 equals 9.  
  10. The result 9 can be devided by 2? False  
  11. 4 can be divided by 3? False  
  12. 5 can be divided by 3? False  
  13. 6 can be divided by 3? True  
  14. The square of 6 equals 36.  
  15. The result 36 can be devided by 2? True  
  16. ===== Print 36 =====  
  17. 7 can be divided by 3? False  
  18. 8 can be divided by 3? False  
  19. 9 can be divided by 3? True  
  20. The square of 9 equals 81.  
  21. The result 81 can be devided by 2? False 

列表中元素的执行顺序是这样的:
***个元素“0”经过Where…Select…Where,***被Print出来。
第二个元素“1”经过Where,中止。
第三个元素“2”经过Where,中止。
第四个元素“4”经过Where…Select…Where,中止。
……

.NET 3.5扩展方法的神奇之处

这说明了,我们使用.NET框架自带的Where或Select方法,最终的效果和上一节中的“合并循环”类似。因为,如果创建了临时容器保存元素的话,就会在***个Where中把所有元素都交由***个委托(i => i % 3 == 0)执行,然后再把过滤后的元素交给Select中的委托(i => i * i)执行。请注意,在这里“合并循环”的效果对外部是隐藏的,我们的代码似乎还是一步一步地处理集合。换句话说,我们使用“分解循环”的清晰方式,但获得了“合并循环”的高效实现。这就是.NET框架这些扩展方法的神奇之处1。

在我们进行具体的性能测试之前,我们再来想一下,这里出现了那么多IEnumerable对象实现了哪个GoF 23中的模式呢?枚举器?看到IEnumerable就说枚举器也太老生常谈了。其实这里同样用到了“装饰器”模式。每次Where或Select之后其实都是使用了一个新的IEnumerable对象来封装原有的对象,这样我们遍历新的枚举器时便会获得“装饰”后的效果。因此,以后如果有人问您“.NET框架中有哪些的装饰器模式的体现”,除了人人都知道的Stream之外,您还可以回答说“.NET 3.5中System.Linq.Enumerable类里的一些扩展方法”,多酷。

以上就介绍了.NET 3.5扩展方法和Lambda表达式实现的高性能分解循环的委托方法。

【编辑推荐】

  1. Lambda表达式:要性能还是要清晰的代码?
  2. .NET Lambda表达式的函数式特性:索引示例
  3. .NET Lambda表达式的语义:字符串列表范例
  4. 使用.NET 3.5 Lambda表达式实现委托
  5. 各版本.NET委托的写法回顾

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...