繁体   English   中英

在关闭警告中访问foreach变量

[英]Access to foreach variable in closure warning

我收到以下警告:

在关闭时访问foreach变量。 使用不同版本的编译器编译时可能会有不同的行为。

这就是我的编辑器中的样子:

悬停弹出窗口中的上述错误消息

我知道怎么修这个警告,但我想知道为什么我会收到这个警告?

这是关于“CLR”版本的吗? 它与“IL”有关吗?

这个警告有两个部分。 首先是......

在关闭时访问foreach变量

......本身并非无效,但乍一看是违反直觉的。 这样做也很难。 (以至于我链接到下面的文章将其描述为“有害”。)

接受您的查询,注意您摘录的代码基本上是C#编译器(在C#5之前)为foreach 1生成的扩展形式:

我[不]理解为什么[以下]无效:

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

嗯,它在语法上是有效的。 如果你在循环中所做s一切都是使用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而不是通过手动枚举产生的。 这就是为什么C#5中foreach的变化有助于防止这个问题,但并非完全无法解决。

第一个答案很棒,所以我想我只想补充一点。

您收到警告,因为在您的示例代码中,reflectModel被分配了一个IEnumerable,它只会在枚举时进行评估,如果您将reflectModel分配给范围更广的内容,枚举本身可能会发生在循环之外。

如果你改变了

...Where(x => x.Name == property.Value)

...Where(x => x.Name == property.Value).ToList()

那么reflectModel将在foreach循环中被分配一个明确的列表,所以你不会收到警告,因为枚举肯定会在循环内发生,而不是在它之外。

块范围的变量应解决警告。

foreach (var entry in entries)
{
   var en = entry; 
   var result = DoSomeAction(o => o.Action(en));
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM