我收到以下警告:
访问闭包中的foreach变量。使用不同版本的编译器进行编译时,可能会有不同的行为。
这是我的编辑器中的样子:

我知道如何解决此警告,但我想知道为什么会收到此警告?
这是关于“ CLR”版本的吗?与“ IL”有关吗?
我收到以下警告:
访问闭包中的foreach变量。使用不同版本的编译器进行编译时,可能会有不同的行为。
这是我的编辑器中的样子:

我知道如何解决此警告,但我想知道为什么会收到此警告?
这是关于“ CLR”版本的吗?与“ IL”有关吗?
Answers:
此警告分为两部分。首先是...
访问闭包中的foreach变量
...这本身不是无效的,但乍看之下是违反直觉的。做正确的事也很困难。(以至于我在下面链接到的文章都将其描述为“有害”。)
进行查询,请注意,您摘录的代码基本上是C#编译器(在C#5之前)为foreach1生成的代码的扩展形式:
我[不]理解[以下内容无效]的原因:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
好吧,在语法上是有效的。而且,如果您在循环中所做的所有事情都使用了value,s那么一切都很好。但是,封闭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在控制台上打印了五次。
正是我们所要的,但可能不是我们想要的。
警告的第二部分...
使用不同版本的编译器进行编译时,可能会有不同的行为。
...就是它了。从C#5开始,编译器会生成不同的代码,以防止通过发生这种情况foreach。
因此,以下代码在不同版本的编译器下将产生不同的结果:
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 Framework版本。
与IL相关吗?
不同的代码会产生不同的IL,因此从某种意义上说,生成的IL会有后果。
1 foreach是比您在评论中发布的代码更常见的构造。该问题通常是通过使用引起的foreach,而不是通过手动枚举引起的。因此,对foreachC#5进行的更改有助于防止此问题,但不能完全避免。
foreach这里的东西来自于问题的内容。没错,它可以以多种更普遍的方式发生。
第一个答案很好,所以我想我只想添加一件事。
之所以收到警告,是因为在示例代码中,reflectedModel被分配了一个IEnumerable,它将仅在枚举时进行评估,并且如果您将reflectingModel分配给范围更广的对象,则枚举本身可能在循环之外发生。
如果你改变了
...Where(x => x.Name == property.Value)
至
...Where(x => x.Name == property.Value).ToList()
则在foreach循环内将为reflectedModel分配一个确定的列表,因此您不会收到警告,因为枚举肯定会在循环内发生,而不是在循环外发生。