我正在检查是否在构建机器上安装.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
  }
}

10-08 09:27