我收到以下警告:
在闭包中访问foreach变量.使用不同版本的编译器编译时,可能会有不同的行为.
这是它在我的编辑器中的样子:
我知道如何修复此警告,但我想知道为什么会收到此警告?
这是关于"CLR"版本的吗?它和"IL"有关吗?
我收到以下警告:
在闭包中访问foreach变量.使用不同版本的编译器编译时,可能会有不同的行为.
这是它在我的编辑器中的样子:
我知道如何修复此警告,但我想知道为什么会收到此警告?
这是关于"CLR"版本的吗?它和"IL"有关吗?
这一警告分为两部分.首先是...
在闭包中访问foreach变量
...这本身并不是无效的,但乍一看是违反直觉的.这也很难做到正确.(以至于我在下面链接的文章将其描述为"有害的".)
以您的查询为例,请注意,您摘录的代码基本上是C#编译器(在C#5之前)为foreach
1生成的代码的扩展形式:
我[不]明白为什么[以下内容]无效:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
这在句法上是有效的.如果你在循环中所做的只是使用s
中的value,那么一切都很好.但接近s
将导致违反直觉的行为.看看下面的代码:
var countingActions = new List<Action>();
var numbers = from n in Enumerable.Range(1, 5)
select n.ToString(CultureInfo.InvariantCulture);
using (var enumerator = numbers.GetEnumerator())
{
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
Console.WriteLine("Creating an action where s == {0}", s);
Action action = () => Console.WriteLine("s == {0}", s);
countingActions.Add(action);
}
}
如果运行此代码,将获得以下控制台输出:
Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5
这就是你所期待的.
要查看您可能没有预料到的内容,请在上面的代码中运行以下代码:
foreach (var action in countingActions)
action();
您将获得以下控制台输出:
s == 5
s == 5
s == 5
s == 5
s == 5
为什么?因为我们创建了五个函数,它们都做完全相同的事情:打印s
的值(我们已经结束了).实际上,它们是相同的功能("打印s
"、"打印s
"、"打印s
"…).
在我们使用它们的时候,它们完全按照我们的要求执行:打印s
的值.如果你看最后一个已知值s
,你会发现它是5
.所以我们在控制台上打印了五次s == 5
.
这正是我们想要的,但可能不是我们想要的.
警告的第二部分...
使用不同版本的编译器编译时,可能会有不同的行为.
因此,以下代码在不同版本的编译器下会产生不同的结果:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
因此,它还将生成R#警告:)
上面我的第一个代码片段将在所有版本的编译器中表现出相同的行为,因为我没有使用foreach
(相反,我已经像C#5之前的编译器一样对其进行了扩展).
这是CLR版本的吗?
我不太清楚你在问什么.
Eric Lippert的帖子称,这种变化发生在"C#5"中.所以大概你必须瞄准目标.NET 4.5或更高版本,使用C#5或更高版本的编译器来获取新行为,以及之前的所有操作都会获取旧行为.
但要明确的是,它是编译器的函数,而不是.NET框架版本.
与IL有关联吗?
不同的代码会产生不同的IL,因此在这个意义上会对生成的IL产生影响.
1 foreach
is a much more common construct than the code you've posted in your comment. The issue typically arises through use of foreach
, not through manual enumeration. That's why the changes to foreach
in C# 5 help prevent this issue, but not completely.