闭包并不是新概念,在 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,而且也很容易收到框架中变化的影响。
查看英文原文: The Dark Side of Closures
活动推荐:
2023年9月3-5日,「QCon全球软件开发大会·北京站」 将在北京•富力万丽酒店举办。此次大会以「启航·AIGC软件工程变革」为主题,策划了大前端融合提效、大模型应用落地、面向 AI 的存储、AIGC 浪潮下的研发效能提升、LLMOps、异构算力、微服务架构治理、业务安全技术、构建未来软件的编程语言、FinOps 等近30个精彩专题。咨询购票可联系票务经理 18514549229(微信同手机号)。
评论