首页 文章

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

提问于
浏览
19

我正在检查在我们的构建机器上安装.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 output :666

VS 2012 output :456

我在VS 2010中创建了一个控制台应用程序,在VS 2012中创建了一个具有相同代码的控制台应用程序(都是针对.NET 4) .

但是,两个控制台应用程序都基于它们构建的IDE表现出不同的行为 . 在构建输出中,我检查了两者都有几乎相似的构建参数 . 所以我想知道最终可执行文件如何表现出不同的行为? .NET 4.5是就地升级,因此两个IDE的编译器必须相同 .

注意:我确实看了一个相关的问题:Different LINQ Answer in VS 2010 and VS 2012但它没有回答我的问题,为什么可执行行为不同 .

EDIT 1:mletterle所述,我确实尝试在VS 2010命令提示符中使用VS 2010的输出窗口中的命令行构建代码 . 结果输出表现得好像是使用VS 2012构建的 .

EDIT 2:

我发布输出窗口中的输出:

VS 2010: Build 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:en-US / highentropyva- / reference:“C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.CSharp.dll”/ reference:“C:\ Program Files (x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ mscorlib.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Core .dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.DataSetExtensions.dll“/ reference:”C:\ Program Files(x86)\参考程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.dll“/参考:“C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ 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 Properties \ 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 studio 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:en-US / highentropyva- / reference:“C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ Microsoft.CSharp.dll”/ reference:“C:\程序文件(x86)\参考程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ mscorlib.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System .Core.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.DataSetExtensions.dll“/ reference:”C:\ Program Files(x86 )\参考程序集\ Microsoft \ Framework.NETFramework \ v4.0 \ System.Data.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.dll “/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.dll“/ reference:”C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xml.Linq.dll“/ debug / debug:full / filealign:512 / o ptimize- /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” .

2 回答

  • 3

    Visual Studio使用in-process compiler,因此它知道它正在使用哪个版本的C# .

    正如您所指出的,另一方面,来自命令行的csc.exe使用的任何C#版本_1858224都将是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(V v in x)嵌入式语句的foreach语句
    然后扩展为:{
    E e =((C)(x)) . GetEnumerator();
    尝试{
    V v;
    while(e.MoveNext()){
    v =(V)(T)e . 当前;
    嵌入语句
    }
    }
    终于{
    ...... //处理e
    }
    }

    Visual Studio 2012中的C#5.0规范说:

    (...)形式为foreach(V v in x)嵌入式语句的foreach语句
    然后扩展为:{
    E e =((C)(x)) . GetEnumerator();
    尝试{
    while(e.MoveNext()){
    V v =(V)(T)e . 当前;
    嵌入语句
    }
    }
    终于{
    ...... //处理e
    }
    }

  • 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不是就地升级 . 它是该工具的不同版本 . 会有分歧 . 这是其中之一 .

    Update 1:

    看起来我们正在使用"in-place"升级的不同定义 . 我对"in-place"的使用是"an upgrade that should have no discernible differences between versions." article you linked to中给出的定义以不同的方式使用它:"in place"在它们的用法中是"uses the same CLR, but adds new libraries."

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

    结果,差异不是CLR你正在瞄准,但你正在使用的语言版本 - CLR是一个“就地”升级(4.0 CLR),但语言不是(VS2010中的C#4,VS2012中的C#5) .

    Update 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工具链的一部分) .

相关问题