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

时间:2014-02-18 09:27:31

标签: c# visual-studio-2010 msbuild

我有以下C#代码:

foreach (var x in m_collection)
{
    m_actionCollection.Add(() =>
    {
        x.DoSomething();
    });
}

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

foreach (var x in m_collection)
{
    m_actionCollection.Add(() =>
    {
        x.DoSomething();
    });
} 

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

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

项目文件包含

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

并且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建立完全相同的环境?

1 个答案:

答案 0 :(得分: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次{1}}。在C#5中,它会在DoSomething的每个项目上调用m_collection一次。

无法确定ILSpy有效反编译的语言版本是什么版本:(

  

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

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

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

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

  

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

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

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

class Test { static async void Foo() {} }

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