我收到以下警告:

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

这是它在我的编辑器中的样子:

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

我知道如何修复此警告,但我想知道为什么会收到此警告?

这是关于"CLR"版本的吗?它和"IL"有关吗?

推荐答案

这一警告分为两部分.首先是...

在闭包中访问foreach变量

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

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

我[不]明白为什么[以下内容]无效:

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.

这正是我们想要的,但可能不是我们想要的.

警告的第二部分...

使用不同版本的编译器编译时,可能会有不同的行为.

...就是这样.Starting with C# 5, the compiler generates different code that "prevents" this from happening via 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框架版本.

与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.

.net相关问答推荐

获取Ef-Core集合的DeleteBehavior

保存时不保留 XML 格式

在本地运行 Azure 函数会在 .NET7 升级后出现无运行时错误

StreamWriter.Flush() 和 StreamWriter.Close() 有什么区别?

如何中止任务,如中止线程(Thread.Abort 方法)?

将客户端证书添加到 .NET Core HttpClient

XmlNode 值与内部文本

(C# 7.2)私有保护修饰符的用例是什么?

我什么时候应该在 C# 中使用使用块?

为什么 Interlocked.Exchange 不支持布尔类型?

什么版本的 .NET 附带什么版本的 Windows?

.NET 反射的成本是多少?

WCF服务客户端:内容类型text/html;响应消息的charset=utf-8 与绑定的内容类型不匹配

仅使用 XAML 绘制纯色三角形

如何在 C# 中以编程方式安装 Windows 服务?

是否可以完全用托管的 .NET 语言编写 JIT 编译器(本地代码)

如何获取命名空间中的所有类?

并发字典正确用法

SqlBulkCopy 的推荐批量大小是多少?

使用 LINQ 搜索树