我正在检查在我们的构建计算机上安装.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;
答案 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 } }