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

Posted

技术标签:

【中文标题】在 VS2010 中或通过 MsBuild 命令行编译解决方案时生成的代码不同【英文标题】:Generated code differs when compiling solution in VS2010 or via MsBuild command line 【发布时间】:2014-02-18 09:27:30 【问题描述】:

我有以下 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】:

我怀疑您看到了 C# 5 编译器(通过 msbuild 调用)和 C# 4 编译器(在 VS2010 中)之间的区别。我有点惊讶 msbuild 使用的是 C# 5 编译器,但它似乎是......

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

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

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

为了通过 MsBuild 命令行和在 VS 2010 中构建完全相同的环境,必须做什么/检查?

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

您可以通过查看 msbuild 日志并查看csc.exe 的哪个版本用于CoreCompile 步骤来检查。一旦您知道正在使用哪个编译器binary,您就可以只从命令行运行编译器来进行您认为合适的修改。

当我使用/toolsversion:4.0 运行msbuild 时,它使用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()  

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

【讨论】:

是的,我知道我的代码被破坏了,但没有它我不会注意到使用了不同的编译器版本:-)。 MsBuild怎么会被强制使用与VS2010相同的编译器版本? 感谢您的详细解释。尽管存在所有版本号混淆,但在哪里可以找到 VS 2010 使用的 .NET 4.0 (C# 4.0) 的 csc.exe? @Harry13:恐怕我不能告诉你——部分原因是我自己没有安装它。 可能它甚至不能以独立的形式提供,但我建议您在您的机器上查找所有出现的csc.exe。同样,如果可能的话,我鼓励你升级:) 它是同一个可执行文件。通过比较 VS 和命令行 MsBuild 的详细 MsBuild 输出发现。同样在命令行参数中似乎与该问题没有什么不同。在命令行构建的 MsBuild 日志中,以下消息似乎是可疑的:使用程序集“Microsoft.Build.Tasks.v4.0,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a”中的“Csc”任务。这只发生在 MsBuild 而不是 VS2010 @Harry13:这非常奇怪。不幸的是,没有安装 VS2010,我将很难重现和进一步挖掘:(

以上是关于在 VS2010 中或通过 MsBuild 命令行编译解决方案时生成的代码不同的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MSBuild 构建具有 VS2010 配置的 VS2015 解决方案?

MSBuild命令行错误 - 未安装Silverlight 4 SDK

如何使用命令行msbuild部署VS2012网站项目而不进行预编译?

MSBuild Script 和 VS2010 发布应用 Web.config 转换

msbuild 命令行尚不支持 WebPublishMethod(FileSystem)

通过 Devenv 命令行构建时禁用代码分析