首页 > C# > 在 C# 中使用 lambda 表达式或匿名方法时,我们必须警惕访问修改后的闭包陷阱

在 C# 中使用 lambda 表达式或匿名方法时,我们必须警惕访问修改后的闭包陷阱

上一篇 下一篇

网友问题:
在 C# 中使用 lambda 表达式或匿名方法时,我们必须警惕访问修改后的闭包陷阱。例如:

foreach (var s in strings)
{
   query = query.Where(i => i.Prop == s); // access to modified closure
   ...
}

由于修改了闭包,上面的代码将导致查询上的所有子句都基于 .Wheres

如此处所述,发生这种情况是因为上面循环中声明的变量在编译器中按如下方式转换:sforeach

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}

而不是这样:

while (enumerator.MoveNext())
{
   string s;
   s = enumerator.Current;
   ...
}

正如这里所指出的,在循环之外声明变量没有性能优势,在正常情况下,我能想到这样做的唯一原因是,如果您打算在循环范围之外使用该变量:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}
var finalString = s;

但是,循环中定义的变量不能在循环外部使用:foreach

foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.

因此,编译器声明变量的方式使其极易出现通常难以查找和调试的错误,同时不会产生可感知的好处。

您是否可以以这种方式对循环做些什么,而如果它们使用内部范围的变量进行编译,则无法做到,或者这只是在匿名方法和 lambda 表达式可用或通用之前做出的任意选择,并且从那时起就没有被修改过?foreach

分割线

网友回答:

Eric Lippert在他的博客文章Closed over the loop variable 被认为是有害的及其续集中彻底涵盖了你所问的问题。

对我来说,最有说服力的论点是,在每次迭代中都有新变量与样式循环不一致。您是否希望在 的每次迭代中都有一个新的?for(;;)int ifor (int i = 0; i < 10; i++)

此行为最常见的问题是对迭代变量进行闭包,它有一个简单的解决方法:

foreach (var s in strings)
{
    var s_for_closure = s;
    query = query.Where(i => i.Prop == s_for_closure); // access to modified closure

我关于这个问题的博客文章:C# 中 foreach 变量的闭包。

分割线

网友回答:

编译器声明变量的方式使其极易出现通常难以查找和调试的错误,同时不会产生可感知的好处。

你的批评是完全有道理的。

我在这里详细讨论这个问题:

关闭循环变量被认为是有害的

是否可以以这种方式对 foreach 循环执行某些操作,而如果它们使用内部范围的变量编译,则无法执行?或者这只是在匿名方法和 lambda 表达式可用或通用之前做出的任意选择,并且从那时起就没有修改过?

后者。C# 1.0 规范实际上没有说明循环变量是在循环体内部还是外部,因为它没有明显的差异。在 C# 2.0 中引入闭包语义时,选择将循环变量放在循环之外,与“for”循环一致。

我认为可以公平地说,所有人都对这一决定感到遗憾。这是 C# 中最糟糕的“陷阱”之一,我们将采用重大更改来修复它。在 C# 5 中,foreach 循环变量在逻辑上位于循环主体,因此闭包每次都会获得一个新副本。

不会更改循环,并且更改不会“向后移植”到以前版本的 C#。因此,在使用这个成语时,你应该继续小心。for

分割线

网友回答:

被这个咬了一口后,我习惯于在最内层的作用域中包含局部定义的变量,我用它来转移到任何闭包。在您的示例中:

foreach (var s in strings)
    query = query.Where(i => i.Prop == s); // access to modified closure

我愿意:

foreach (var s in strings)
{
    string search = s;
    query = query.Where(i => i.Prop == search); // New definition ensures unique per iteration.
}        

一旦你养成了这个习惯,你就可以在少数情况下避免它,你实际上打算绑定到外部范围。老实说,我认为我从来没有这样做过。

模板简介:该模板名称为【在 C# 中使用 lambda 表达式或匿名方法时,我们必须警惕访问修改后的闭包陷阱】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 264次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-09
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
自适应 微信图片 微信模板 微信公众平台 企业网站 微信素材 微信文章 响应式 单页式简历模板 html5
您可能会喜欢的其他模板