我正在检查是否在构建机器上安装.NET 4.5会更改VS 2010生成的输出IL映像。
因为我知道.NET 4.5中foreach的行为已更改以避免出现Access to Modified closure问题,所以我选择了一个表现出此行为的简单应用程序。
class Program
{
private static void Main(string[] args)
{
var contents = new List<Func<int>>();
var s = new StringBuilder();
int[] values = new int[] { 4, 5, 6 };
foreach (int value in values)
{
contents.Add(() => value);
}
for (var k = 0; k < contents.Count; k++)
s.Append(contents[k]());
Console.WriteLine(s);
}
VS 2010输出:666
VS 2012输出:456
我在VS 2010中创建了一个控制台应用程序,并在VS 2012中创建了具有相同代码的控制台应用程序(均针对.NET 4)。
但是,两个控制台应用程序都基于构建它们的IDE表现出不同的行为。在构建输出中,我检查了两者是否具有几乎相似的构建参数。所以我想知道最终可执行文件如何表现出不同的行为? .NET 4.5是就地升级,因此两个IDE的编译器必须相同。
注意:我确实看过一个相关的问题:Different LINQ Answer in VS 2010 and VS 2012,但是它没有回答我关于可执行行为为何不同的问题。
编辑1:
如mletterle所述,我确实尝试在VS 2010命令提示符下的VS 2010输出窗口中使用命令行构建代码。结果输出的行为就像是使用VS 2012构建的一样。
编辑2:
我将输出显示在“输出窗口”中:
VS 2010:
建立开始于12/20/2012 11:04:56 PM。
CoreClean:创建目录“ obj \ x86 \ Debug \”。
GenerateTargetFrameworkMonikerAttribute:跳过目标
“ GenerateTargetFrameworkMonikerAttribute”,因为所有输出文件都是
有关输入文件的最新信息。 CoreCompile:
C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ Csc.exe / noconfig
/ nowarn:1701,1702 / nostdlib + / platform:x86 / errorreport:prompt / warn:4
/ define:DEBUG; TRACE / errorendlocation / preferreduilang:zh-CN
/ highentropyva- / reference:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.CSharp.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ mscorlib.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Core.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.DataSetExtensions.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.dll“
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.Linq.dll”
/ debug + / debug:full / filealign:512 / optimize-
/out:obj\x86\Debug\TestConsoleApp.exe / target:exe / utf8output
Program.cs属性\ AssemblyInfo.cs
“ C:\ Users \ 105044960 \ AppData \ Local \ Temp.NETFramework,Version = v4.0.AssemblyAttributes.cs”
_CopyAppConfigFile:跳过目标“ _CopyAppConfigFile”,因为所有输出文件都相对于输入文件是最新的。
CopyFilesToOutputDirectory:从中复制文件
从“ obj \ x86 \ Debug \ TestConsoleApp.exe”到“ bin \ Debug \ TestConsoleApp.exe”。
TestConsoleApp-> C:\ Users \ 105044960 \ Documents \ Visual Studio
2010 \ Projects \ TestConsoleApp \ TestConsoleApp \ bin \ Debug \ TestConsoleApp.exe
将文件从“ obj \ x86 \ Debug \ TestConsoleApp.pdb”复制到
“ bin \ Debug \ TestConsoleApp.pdb”。
VS 2012:
1> CoreClean:1>删除文件“ c:\ users \ 105044960 \ documents \ visual
工作室
11 \ Projects \ TestConsoleApp \ TestConsoleApp \ bin \ Debug \ TestConsoleApp.exe”。
1>删除文件“ c:\ users \ 105044960 \ documents \ visual studio
11 \ Projects \ TestConsoleApp \ TestConsoleApp \ bin \ Debug \ TestConsoleApp.pdb”。
1>删除文件“ c:\ users \ 105044960 \ documents \ visual studio
11 \ Projects \ TestConsoleApp \ TestConsoleApp \ obj \ Debug \ TestConsoleApp.csprojResolveAssemblyReference.cache”。
1>删除文件“ c:\ users \ 105044960 \ documents \ visual studio
11 \ Projects \ TestConsoleApp \ TestConsoleApp \ obj \ Debug \ TestConsoleApp.exe”。
1>删除文件“ c:\ users \ 105044960 \ documents \ visual studio
11 \ Projects \ TestConsoleApp \ TestConsoleApp \ obj \ Debug \ TestConsoleApp.pdb”。
1> GenerateTargetFrameworkMonikerAttribute:1>跳过目标
“ GenerateTargetFrameworkMonikerAttribute”,因为所有输出文件都是
有关输入文件的最新信息。 1> CoreCompile:1>
C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ Csc.exe / noconfig
/ nowarn:1701,1702,2008 / nostdlib + / platform:AnyCPU / errorreport:prompt
/ warn:4 / define:DEBUG; TRACE / errorendlocation / preferreduilang:zh-CN
/ highentropyva- / reference:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.CSharp.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ mscorlib.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Core.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.DataSetExtensions.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.dll“
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.dll”
/引用:“ C:\ Program Files(x86)\ Reference
程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.Linq.dll”
/ debug + / debug:full / filealign:512 / optimize-
/out:obj\Debug\TestConsoleApp.exe / target:exe / utf8output Program.cs
Properties \ AssemblyInfo.cs
“ C:\ Users \ 105044960 \ AppData \ Local \ Temp.NETFramework,Version = v4.0.AssemblyAttributes.cs”
1> CopyFilesToOutputDirectory:1>从以下位置复制文件
从“ obj \ Debug \ TestConsoleApp.exe”到“ bin \ Debug \ TestConsoleApp.exe”。 1>
TestConsoleApp-> C:\ Users \ 105044960 \ Documents \ Visual Studio
11 \ Projects \ TestConsoleApp \ TestConsoleApp \ bin \ Debug \ TestConsoleApp.exe
1>将文件从“ obj \ Debug \ TestConsoleApp.pdb”复制到
“ bin \ Debug \ TestConsoleApp.pdb”。
最佳答案
Visual Studio使用in-process compiler,因此它知道所使用的C#版本。
如您所述,另一方面,命令行中的csc.exe使用编译时使用的C#版本,因此在您的情况下为C#5.0。由于它是就地升级(就安装目录而言),因此它可能会破坏依赖foreach
绑定的代码在整个循环中都是相同的(奇数,但可能)。
注意:错误答案的旧答案:OP知道这一点,并正在从命令行对其进行测试。
您链接到的博客文章已经回答了您的问题。我认为这个问题与this one有关。
更改的是编译器,因此:
foreach (int value in values)
{
// ...
}
用于根据以下代码生成内容:
{
int value;
for (/* iteration */)
{
value = /* get from enumerator */;
// ...
}
}
而新的C#编译器现在生成将变量移入循环内部的等效功能:
for (/* iteration */)
{
int value = /* get from enumerator */;
// ...
}
这有很大的不同,因为
// ...
中的闭包将在每个循环中捕获一个新的value
绑定,而不是共享以前在循环外部声明的相同value
绑定。要注意的是,如果您希望代码对于旧版本和新版本的编译器都能正常工作,则必须在
foreach
循环内声明自己的变量:foreach (int value in values)
{
int newValue = value;
// ...
}
Visual Studio 2010中当前的C#4.0规范指出:
(...)形式的foreach语句
foreach (V v in x) embedded-statement
然后扩展为:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Visual Studio 2012中的C#5.0规范说:
(...)形式的foreach语句
foreach (V v in x) embedded-statement
然后扩展为:
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}