在VS2010中编译解决方案或通过MsBuild命令行生成的代码不同

Har*_*y13 5 c# msbuild visual-studio-2010

我有以下C#代码:

foreach (var x in m_collection)
{
    m_actionCollection.Add(() =>
    {
        x.DoSomething();
    });
}
Run Code Online (Sandbox Code Playgroud)

如果我在VS2010中编译解决方案,则会生成以下代码(使用IlSpy进行反编译):

foreach (var x in m_collection)
{
    m_actionCollection.Add(() =>
    {
        x.DoSomething();
    });
} 
Run Code Online (Sandbox Code Playgroud)

如果我通过MsBuild命令行编译解决方案,则会生成以下代码:

using (List<X>.Enumerator enumerator = this.m_collection.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        X x = enumerator.Current;
        m_actionCollection.Add(() =>
        {
            x.DoSomething();
        });         
    }
}
Run Code Online (Sandbox Code Playgroud)

项目文件包含

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
Run Code Online (Sandbox Code Playgroud)

并且MsBuild命令行如下所示: C:\Windows\Microsoft.NET\Framework\v4.0.30319\MsBuild.exe Solution.sln /property:Configuration=Debug /property:Platform=x86 /maxcpucount /nologo /toolsversion:4.0

所以我假设编译器是相同的,但它不是......

必须完成/检查才能通过MsBuild命令行和VS 2010建立完全相同的环境?

Jon*_*eet 9

我怀疑你看到C#5编译器(通过msbuild调用)和C#4编译器(VS2010)之间的区别.我对msbuild使用C#5编译器感到有些惊讶,但似乎......

如何foreach捕获迭代变量的规则在C#4和C#5之间发生了变化.在C#5中,循环的每次迭代都foreach有效地捕获了一个不同的变量.在C#4中,所有迭代都捕获相同的变量.

这意味着您的原始代码在C#4中被有效地破解,但在C#5中没有问题.在C#4中,如果您之后执行所有操作m_actionCollection,它会很好,它会调用DoSomething()最后一项m_collection很多次(m_collection.Count次).在C#5中,它会DoSomething在每个项目上调用一次m_collection.

没有帮助,不清楚ILSpy的语言版本有效反编译为:(

必须完成/检查才能通过MsBuild命令行和VS 2010建立完全相同的环境?

我强烈建议最简单的方法是转而使用VS2012或VS2013.

您可以通过查看msbuild日志来查看,并查看该步骤csc.exe使用的版本CoreCompile.一旦知道正在使用哪个编译器二进制文件,就可以根据需要从命令行运行编译器以进行修补.

当我msbuild使用/toolsversion:4.0它时c:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe,它本身会报告:

Microsoft(R)Visual C#编译器版本4.0.30319.33440(适用于Microsoft(R).NET Framework 4.5)

这似乎是一个C#5编译器,尽管名称.我怀疑这是因为.NET 4.5是就地升级.

检查您真正使用的编译器的最简单方法是尝试包含async方法.例如,这是一个完整的类:

class Test { static async void Foo() {} }
Run Code Online (Sandbox Code Playgroud)

如果编译时会发出警告 CS1998("此async方法缺少等待运算符......")那么它正在使用C#5编译器.较旧的编译器会给出错误(可能是"Invalid token'void'...")