为什么在VS 2010 v.s.中构建的应用程序的行为存在差异。 VS 2012?

时间:2012-12-20 16:55:41

标签: .net visual-studio-2010 visual-studio-2012

我正在检查在我们的构建计算机上安装.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: Build in 12/20/2012 11:04:56 PM。

  

CoreClean:创建目录&#34; obj \ x86 \ Debug \&#34;。   GenerateTargetFrameworkMonikerAttribute:跳过目标   &#34; GenerateTargetFrameworkMonikerAttribute&#34;因为所有输出文件都是   关于输入文件的最新信息。 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:en-US   / highentropyva- / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ Microsoft.CSharp.dll&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ mscorlib.dll中&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Core.dll&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Data.DataSetExtensions.dll&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Data.dll中&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.dll中&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ system.xml.dll的&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Xml.Linq.dll&#34;   / debug + / debug:full / filealign:512 / optimize-   /out:obj\x86\Debug\TestConsoleApp.exe / target:exe / utf8output   Program.cs Properties \ AssemblyInfo.cs   &#34; C:\用户\ 105044960 \应用程序数据\本地\ Temp.NETFramework,版本= v4.0.AssemblyAttributes.cs&#34;   _CopyAppConfigFile:跳过目标&#34; _CopyAppConfigFile&#34;因为所有输出文件都是与输入文件相关的最新文件。   CopyFilesToOutputDirectory:从中复制文件   &#34;物镜\ 86 \调试\ TestConsoleApp.exe&#34; to&#34; bin \ Debug \ TestConsoleApp.exe&#34;。   TestConsoleApp - &gt; C:\ Users \ 105044960 \ Documents \ Visual Studio   2010 \项目\ TestConsoleApp \ TestConsoleApp \ BIN \调试\ TestConsoleApp.exe   从&#34; obj \ x86 \ Debug \ TestConsoleApp.pdb&#34;复制文件至   &#34; BIN \调试\ TestConsoleApp.pdb&#34;

VS 2012:

  

1&gt; CoreClean:1&gt;删除文件&#34; c:\ users \ 105044960 \ documents \ visual   工作室   11 \项目\ TestConsoleApp \ TestConsoleApp \ BIN \调试\ TestConsoleApp.exe&#34 ;.   1 GT;删除文件&#34; c:\ users \ 105044960 \ documents \ visual studio   11 \项目\ TestConsoleApp \ TestConsoleApp \ BIN \调试\ TestConsoleApp.pdb&#34 ;.   1 GT;删除文件&#34; c:\ users \ 105044960 \ documents \ visual studio   11 \项目\ TestConsoleApp \ TestConsoleApp \ OBJ \调试\ TestConsoleApp.csprojResolveAssemblyReference.cache&#34 ;.   1 GT;删除文件&#34; c:\ users \ 105044960 \ documents \ visual studio   11 \项目\ TestConsoleApp \ TestConsoleApp \ OBJ \调试\ TestConsoleApp.exe&#34 ;.   1 GT;删除文件&#34; c:\ users \ 105044960 \ documents \ visual studio   11 \项目\ TestConsoleApp \ TestConsoleApp \ OBJ \调试\ TestConsoleApp.pdb&#34 ;.   1&gt; GenerateTargetFrameworkMonikerAttribute:1&gt;跳过目标   &#34; GenerateTargetFrameworkMonikerAttribute&#34;因为所有输出文件都是   关于输入文件的最新信息。 1&gt; CoreCompile:1&gt;   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:en-US   / highentropyva- / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ Microsoft.CSharp.dll&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ mscorlib.dll中&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Core.dll&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Data.DataSetExtensions.dll&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Data.dll中&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.dll中&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ system.xml.dll的&#34;   / reference:&#34; C:\ Program Files(x86)\ Reference   大会\微软\ Framework.NETFramework \ V4.0 \ System.Xml.Linq.dll&#34;   / debug + / debug:full / filealign:512 / optimize-   /out:obj\Debug\TestConsoleApp.exe / target:exe / utf8output Program.cs   属性\ AssemblyInfo.cs中   &#34; C:\用户\ 105044960 \应用程序数据\本地\ Temp.NETFramework,版本= v4.0.AssemblyAttributes.cs&#34;   1&gt; CopyFilesToOutputDirectory:1&gt;从中复制文件   &#34;物镜\调试\ TestConsoleApp.exe&#34; to&#34; bin \ Debug \ TestConsoleApp.exe&#34;。 1 GT;   TestConsoleApp - &gt; C:\ Users \ 105044960 \ Documents \ Visual Studio   11 \项目\ TestConsoleApp \ TestConsoleApp \ BIN \调试\ TestConsoleApp.exe   1 GT;从&#34; obj \ Debug \ TestConsoleApp.pdb&#34;复制文件至   &#34; BIN \调试\ TestConsoleApp.pdb&#34;

2 个答案:

答案 0 :(得分:8)

注意:我删除了原始回复的大部分内容。它回答了错误的问题。随后会有更好的回应。

啊,现在我看到你在问什么:“安装.NET 4.5后,Visual Studio 2010如何知道编译到C#4而不是C#5,即使Visual Studio 2010和Visual Studio 2012也使用相同的csc。 exe并将相同的选项传递给它?“

  

@mletterle但.NET 4.5是对.NET 4的适当升级。所以我的机器上只有.NET 4。唯一的可能是IDE隐藏了一些我看不到的.NET 4编译器的隐藏副本。

我不确定你在哪里听到这个或你为什么这么认为。 .NET 4.5不是就地升级。它是该工具的不同版本。会有分歧。这是其中之一。

更新1:

看起来我们使用了不同的“就地”升级定义。我对“就地”的使用是“升级,版本之间应该没有明显的区别。” article you linked to中给出的定义以不同的方式使用它:在其使用中“就地”是“使用相同的CLR,但添加了新的库”。

由于C#5与C#4不同,因此在我熟悉的用法中,该更改并非“到位”。

因此,差异不是您要定位的CLR,而是您正在使用的语言版本 - CLR是“就地”升级(均为4.0 CLR),但语言不是(C# VS2010中的4,VS2012中的C#5。)

更新2:

在.csproj文件(实际上是由Visual Studio管理的msbuild文件)中,有一个属性指定目标框架。默认情况下,使用Visual Studio 2012制作的项目具有此功能:

<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>

Visual Studio 2010中针对版本4的项目如下所示:

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

这告诉Visual Studio在构建一个或另一个目标框架时设置环境。虽然看起来直接从命令提示符调用csc.exe,但实际上并非如此:msbuild项目实际上是正在处理的内容,它发生在“Visual Studio”的自定义进程环境中。

我只能假设发生了什么,但可能在升级之后,将“TargetFrameworkVersion”属性设置为v4.0会在编译面向v4.0的项目期间将环境返回到v4.0。另一方面,通过从命令行调用csc.exe而没有msbuild设置的环境,它使用其版本的“默认值”(现在默认为C#5),即使你使用了新的C#5行为'使用VS 2010命令提示符。但是,当您通过MSBuild调用构建时,它知道如何在构建期间返回到原始C#4环境(因为MSBuild也是.NET工具链的一部分。)

答案 1 :(得分:3)

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
  }
}