Access to foreach variable in closure warning
我收到以下警告:
Access to foreach variable in closure. May have different behaviour when compiled with different versions of compiler.
在我的编辑器中是这样的:
我知道如何修复此警告,但我想知道为什么会收到此警告?
这是关于"clr"版本的吗?它与"IL"有关吗?
此警告分为两部分。第一个是…
Access to foreach variable in closure
…这本身并不是无效的,但乍一看却是反直觉的。也很难做到正确。(如此之多以至于我在下面链接到的文章将其描述为"有害的"。)
以您的查询为例,注意您摘录的代码基本上是C编译器(在C 5之前)为
I [don't] understand why [the following is] not valid:
1 string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
它在语法上是有效的。如果你在循环中所做的就是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); } } |
如果运行此代码,将获得以下控制台输出:
1 2 3 4 5 | 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 |
这就是你所期望的。
要查看您可能不希望看到的内容,请在上述代码之后立即运行以下代码:
1 2 | foreach (var action in countingActions) action(); |
您将得到以下控制台输出:
1 2 3 4 5 | s == 5 s == 5 s == 5 s == 5 s == 5 |
为什么?因为我们创建了五个完全相同的函数:打印
在我们使用它们的时候,它们完全按照我们的要求执行:打印
这正是我们要求的,但可能不是我们想要的。
警告的第二部分…
May have different behaviour when compiled with different versions of compiler.
…就是这样。从C_5开始,编译器生成不同的代码,以"防止"通过
因此,以下代码将在不同版本的编译器下产生不同的结果:
1 2 3 4 5 | foreach (var n in numbers) { Action action = () => Console.WriteLine("n == {0}", n); countingActions.Add(action); } |
因此,它也会产生R警告:)
我上面的第一个代码片段在所有版本的编译器中都会表现出相同的行为,因为我没有使用
Is this for CLR version?
我不太确定你在问什么。
埃里克·利珀特的文章说这种变化"发生在C 5"。因此,假设您必须以.NET 4.5或更高版本为目标,使用C 5或更高版本的编译器来获取新行为,以及获取旧行为之前的所有内容。
但要明确的是,它是编译器的函数,而不是.NET框架版本。
Is there relevance with IL?
不同的代码产生不同的IL,因此从这个意义上说,会对生成的IL产生影响。
1
第一个答案很好,所以我想我只增加一件事。
您收到警告是因为在示例代码中,ReflectedModel被分配了一个IEnumerable,该IEnumerable只能在枚举时进行计算,如果将ReflectedModel分配给范围更广的对象,则枚举本身可能会在循环之外发生。
如果你改变了
到
然后,ReflectedModel将在foreach循环中被分配一个明确的列表,这样您就不会收到警告,因为枚举肯定会在循环中发生,而不是在循环之外。
块范围变量应解决警告。
1 2 3 4 5 | foreach (var entry in entries) { var en = entry; var result = DoSomeAction(o => o.Action(en)); } |