Life is random

人生是一次小概率事件

导航

C#和闭包

什么是闭包?

简单来讲,闭包允许你将一些行为封装,将它像一个对象一样传来递去,而且它依然能够访问到原来第一次声明时的上下文。这样可以使控制结构、逻辑操作等从调用细节中分离出来。访问原来上下文的能力是闭包区别一般对象的重要特征,尽管在实现上只是多了一些编译器技巧。

[C#和Java的闭包:http://www.zxbc.cn/html/20080516/34373.html]

闭包的副作用

 闭包并不是新概念,在LINQ的使用中已经证明了它难以置信地实用。但是,在它使用时如果破坏了封装,确实会带来明显的副作用。当把两个似乎无关的功能放在一起使用,就会出现意料不到的结果。

闭包允许函数把它们的本地变量共享给定义在这些函数内部的匿名函数。这些匿名函数通常被称作lambda表达式,对于创建由LINQ暴露的强类型查询语句来说是必不可缺的。

Dustin Campbell在对LINQ的实验中,发现查询语句在执行的时候能够被改变。的确如此,通过修改用在闭包代码中的本地变量,用于while子句的功能也就被修改了。如果这是在查询语句执行的时候改变的话,那么相应的查询语句也会有对应的变化和结果。

Dustin使用这个技巧去创建一个仅仅返回不重复项目的查询语句。最初,where子句是"m.Name != filter"。每次当一个条目被返回,filter的值随着条目的值改变。在这种情况下,查询语句能够成功地创建一个不重复的列表。
然而,这个技巧也是及其脆弱的。在Dustin的例子中,列表必须在where子句调用前先排好序。如果不这样做,过滤操作会在所有条目被返回前触发,这样也就没有机会去改变查询语句的行为了。因为where和order by子句可以以任意次序先后出现,这个功能在LINQ中是支持的。

Dustin没有提到的是,这个情况不会出现在所有的LINQ Provider中。那些要把查询语句交给像LINQ to SQL这样外部语法引擎的Provider,就没有机会改变where子句了。如果采用并行LINQ(Parallel LINQ),情况会更糟,因为在多个线程运行的时候,任何对where子句的改变都会引发竞态条件(race condition)。

当然,正确的方法应该是只调用Disctinct()就可以了。尽管这些技巧在理论上很有意思,但它们肯定会引发一些微妙的bug,而且也很容易收到框架中变化的影响。

[作者 Jonathan Allen译者 王正]闭包的副作用:http://www.infoq.com/cn/news/2007/09/Closures-Dark-Side

 

posted on 2008-10-20 19:00  RandomLife  阅读(3403)  评论(0编辑  收藏  举报