为什么我会得到“lambda表达式中的迭代变量可能会产生意外结果”的提示?

4

可能重复:
为什么在 lambda 表达式中使用迭代变量是不好的?

为什么会出现“在 lambda 表达式中使用迭代变量可能会产生意外结果”的错误?假设我有以下代码:

  Dim writeAbleColumns As String() = {"IsSelected", "IsFeeExpense", "IsSubscriptionRedemption"}
  With grid
     For Each column As DataGridViewColumn In .Columns
      column.ReadOnly = Not Array.Exists(writeAbleColumns, Function(arrElement) column.Name = arrElement)
      Next
  End With

我收到了警告:

Warning 1   Using the iteration variable in a lambda expression may have unexpected results.  Instead, create a local variable within the loop and assign it the value of the iteration variable.

我不明白为什么将我的代码更改为以下内容会改变任何内容:

  Dim writeAbleColumns As String() = {"IsSelected", "IsFeeExpense", "IsSubscriptionRedemption"}
  With grid
     For Each column As DataGridViewColumn In .Columns
      Dim c As DataGridViewColumn = column
      column.ReadOnly = Not Array.Exists(writeAbleColumns, Function(arrElement) c.Name = arrElement)
      Next
  End With

基本上什么都没有改变,除了警告消失了。我只是让另一个变量指向我的变量。为什么会有警告?可能会发生哪些意外情况?
2个回答

15

lambda表达式绑定到变量,而不是将lambda转换为委托时变量的值。随着循环变量的更新,绑定到该变量的每个lambda也会看到变量的更改值,这可能不是您想要的。在每次迭代循环时创建一个新变量,可以将每个lambda绑定到一个新的、不同的、不可改变的变量。

这是C#和VB中的主要痛点。 在C# 5和VB 11中,我们正在更改循环闭包语义以减轻这个问题。

有关详细信息,请参见

Is there a reason for C#'s reuse of the variable in a foreach?

以及Tim文章的最后几段:

http://msdn.microsoft.com/en-us/magazine/cc163362.aspx

和我的文章:

http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/


我们能否也得到C#3和C#4编译器的补丁呢?我每天都会遇到这个问题,而我们公司现在才开始使用C#4。我真的等不及C#5了。 - James Dunne
@JamesDunne,你确定你说的是C#的版本而不是.NET的版本吗?因为C# 5编译器可以针对.NET 4.0甚至更早的版本进行编译。 - svick
1
@svick:当然,编译器可以针对旧平台进行目标设置,但是当您希望使用Visual Studio、一套TFS构建服务器和所有团队的编译器仍然针对旧平台使用最新版本时,这就不相关了。实际上,获取旧编译器版本的(希望是免费的)补丁比购买整个套件的最新版本以修复此错误更容易和更便宜。 - James Dunne
3
@JamesDunne:我们没有计划悄悄地改变现有编译器的行为。很抱歉! - Eric Lippert

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接