我有以下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建立完全相同的环境?
答案 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'...”)