Dot-Net

為什麼在 VS 2010 和 VS 2012 中建構的應用程序的行為存在差異?

  • December 20, 2012

我正在檢查在我們的建構機器上安裝 .NET 4.5 是否會更改 VS 2010 生成的輸出 IL 映像。

由於我知道 foreach 的行為在 .NET 4.5 中發生了變化,以避免由於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 的編譯器必須相同。

注意:我確實看過一個相關問題:VS 2010 和 VS 2012 中的不同 LINQ 答案,但它沒有回答我關於為什麼可執行行為不同的問題。

編輯 1: 正如mletterle提到的,我確實嘗試在 VS 2010 命令提示符下使用 VS 2010 輸出視窗中的命令行建構程式碼。生成的輸出就像是用 VS 2012 建構的一樣。

編輯2:

我在輸出視窗中發布輸出:

VS 2010: 建構開始於 2012 年 12 月 20 日晚上 11:04:56。

CoreClean:創建目錄“obj\x86\Debug\”。GenerateTargetFrameworkMonikerAttribute:跳過目標“GenerateTargetFrameworkMonikerAttribute”,因為所有輸出文件相對於輸入文件都是最新的。核心編譯:

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)\Reference Assemblies \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 /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”。

與 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:\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:

Visual Studio 使用程序內編譯器,因此它知道它使用的是哪個版本的 C#。

正如您所指出的,另一方面,命令行中的 csc.exe 使用它要編譯的任何 C# 版本,因此在您的情況下它將是 C# 5.0。由於它是就地升級(就安裝目錄而言),它可能會破壞依賴於foreach綁定在整個循環中相同的程式碼(奇怪,但可能)。


注意:錯誤問題的舊答案:OP 知道這一點並正在從命令行對其進行測試。

您連結到的部落格文章已經回答了您的問題。我認為這個問題與這個有關。

改變的是編譯器,所以:

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

注意:我刪除了大部分原始回复。它回答了錯誤的問題。接下來是更好的回應。

啊,現在我明白你在問什麼:“Visual Studio 2010 如何知道在安裝 .NET 4.5 後編譯為 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:

看起來我們使用了不同的“就地”升級定義。我對“就地”的使用是“版本之間應該沒有明顯差異的升級”。您連結到的文章中給出的定義以不同的方式使用它:“就地”在它們的用法中是“使用相同的 CLR,但添加了新的庫”。

由於 C# 5 與 C# 4 不同,因此在我熟悉的用法中,這種更改並不是“到位”的。

結果,差異不是您所針對的 CLR,而是您正在使用的語言版本 - CLR 是“就地”升級(都是 4.0 CLR),但語言不是(VS2010 中的 C# 4 , VS2012 中的 C#5。)

更新 2:

在 .csproj 文件(實際上是由 Visual Studio 管理的 msbuild 文件)中,有一個指定目標框架的屬性。預設情況下,使用 Visual Studio 2012 製作的項目具有以下功能:

<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>

而針對版本 4 的 Visual Studio 2010 中的項目如下所示:

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

這告訴 Visual Studio 在為一個或另一個目標框架建構時設置環境。雖然看起來 csc.exe 是直接從命令提示符呼叫的,但實際上並非如此:msbuild 項目實際上是正在處理的內容,並且它發生在“Visual Studio”的自定義程序環境中。

我只能假設正在發生的事情的細節,但很可能在升級之後,將“TargetFrameworkVersion”屬性設置為 v4.0 在編譯針對 v4.0 的項目期間會將環境返回到 v4.0。另一方面,通過在沒有 msbuild 設置的環境的情況下從命令行呼叫 csc.exe,它使用其版本的“預設值”(現在預設為 C# 5)為您提供新的 C#5 行為,即使您’正在使用 VS 2010 命令提示符。但是,當您通過 MSBuild 呼叫建構時,它知道如何在建構期間返回原始 C# 4 環境(因為 MSBuild 也是 .NET 工具鏈的一部分。)

引用自:https://stackoverflow.com/questions/13976934