.NET Framework 4 的一個兼容性問題


測試程序

我們來看看下面這個非常簡單的 C# 程序 Tester.cs:

 1 using System;
 2 
 3 static class Tester
 4 {
 5   static void Main()
 6   {
 7     Console.WriteLine("   CLR: " + Environment.Version);
 8     Console.WriteLine("Concat: " + string.Concat(new int[]{12, 345}));
 9   }
10 }

這個測試程序的實質內容只有一行,就是第 8 行,調用了 string 類的靜態方法 Concat。

運行結果

我們來看看上述測試程序的運行結果,首先是 Linux 操作系統的 Mono 環境:

ben@vbox:~/work> gmcs Tester.cs && mono Tester.exe
   CLR: 2.0.50727.1433
Concat: System.Int32[]
ben@vbox:~/work> dmcs Tester.cs && mono Tester.exe
   CLR: 4.0.30319.1
Concat: 12345

還有 Windows 操作系統的 Microsoft .NET Framework 環境:

D:\work> C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe Tester.cs
Microsoft (R) Visual C# 2005 編譯器 版本 8.00.50727.4016
用於 Microsoft (R) Windows (R) 2005 Framework 版本 2.0.50727
版權所有(C) Microsoft Corporation 2001-2005。保留所有權利。

D:\work> Tester.exe
   CLR: 2.0.50727.4216
Concat: System.Int32[]

D:\work> C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Tester.cs
Microsoft(R) Visual C# 2010 編譯器 4.0.30319.1 版
版權所有(C) Microsoft Corporation。保留所有權利。

D:\work> Tester.exe
   CLR: 4.0.30319.276
Concat: 12345

看出來了吧,同一個 C# 程序,不論是 Linux 操作系統還是 Windows 操作系統,在 .NET Framework 2.0 和 .NET Framework 4 之間都表現出了不兼容性。

分析

上述運行結果主要是由於 string 類的靜態方法 Concat 在 .NET Framework 2.0 中有 9 種重載的版本,而在 .NET Framework 4 中有 11 種重載的版本。上述測試用的 C# 程序實際調用的重載版本如下所示:

.NET Framework 2.0: public static string Concat(Object arg0)
.NET Framework 4  : public static string Concat<T>(IEnumerable<T> values)

看來,往 .NET Framework Base Class Library 中增加新的方法也不是安全的,有可能改變原有的 C# 程序的行為。這提醒我們將原有的程序從 .NET Framework 2.0 往 .NET Framework 4 遷移時要多加小心,不光要注意到 MSDN 中明確指出“版本注意事項”的地方,也要注意這種微妙的改變。

查看 IL 代碼

我們在 Linux 操作系統下執行以下命令來反匯編 Tester.exe 為 ILASM 源代碼文件:

ben@vbox:~/work> gmcs Tester.cs && monodis Tester.exe > Tester-v2.il
ben@vbox:~/work> dmcs Tester.cs && monodis Tester.exe > Tester-v4.il

下面就是 Tester-v2.il 文件:

 1 .assembly extern mscorlib
 2 {
 3   .ver 2:0:0:0
 4   .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
 5 }
 6 .assembly 'Tester'
 7 {
 8   .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::'.ctor'() =  (
 9                 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
10                 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01       ) // ceptionThrows.
11 
12   .hash algorithm 0x00008004
13   .ver  0:0:0:0
14 }
15 .module Tester.exe // GUID = {9F6A8C8F-B962-4E6A-A4E9-FA6FF9E83982}
16 
17 
18   .class private auto ansi abstract sealed beforefieldinit Tester
19         extends [mscorlib]System.Object
20   {
21 
22     // method line 1
23     .method private static hidebysig
24            default void Main ()  cil managed
25     {
26         // Method begins at RVA 0x20ec
27         .entrypoint
28         // Code size 60 (0x3c)
29         .maxstack 8
30         IL_0000:  ldstr "   CLR: "
31         IL_0005:  call class [mscorlib]System.Version class [mscorlib]System.Environment::get_Version()
32         IL_000a:  call string string::Concat(object, object)
33         IL_000f:  call void class [mscorlib]System.Console::WriteLine(string)
34         IL_0014:  ldstr "Concat: "
35         IL_0019:  ldc.i4.2
36         IL_001a:  newarr [mscorlib]System.Int32
37         IL_001f:  dup
38         IL_0020:  ldc.i4.0
39         IL_0021:  ldc.i4.s 0x0c
40         IL_0023:  stelem.i4
41         IL_0024:  dup
42         IL_0025:  ldc.i4.1
43         IL_0026:  ldc.i4 345
44         IL_002b:  stelem.i4
45         IL_002c:  call string string::Concat(object)
46         IL_0031:  call string string::Concat(string, string)
47         IL_0036:  call void class [mscorlib]System.Console::WriteLine(string)
48         IL_003b:  ret
49     } // end of method Tester::Main
50 
51   } // end of class Tester

再使用 diff 命令來看看 Tester-v4.il 與 Tester-v2.il 的不同之處:

ben@vbox:~/work> diff Tester-v2.il Tester-v4.il
3c3
<   .ver 2:0:0:0
---
>   .ver 4:0:0:0
15c15
< .module Tester.exe // GUID = {9F6A8C8F-B962-4E6A-A4E9-FA6FF9E83982}
---
> .module Tester.exe // GUID = {D564CE72-73D3-43DD-B6F4-FB12809D5589}
45c45
<       IL_002c:  call string string::Concat(object)
---
>       IL_002c:  call string string::Concat<int32> (class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)

可以看出,總共只有三處不同:

  1. 第3行的不同說明了不同的 .NET Framework 版本。
  2. 第15行的不同只是說明了不同的 Tester.exe 的 GUID 不同。
  3. 第45行的不同說明了調用的 Concat 方法的重載版本不同。

上述第3點不同正好驗證了我們在上一節中的分析,明確顯示出了相應調用的 Concat 方法的不同的重載版本。

參考資料

  1. MSDN: String.Concat 方法 (System)
  2. MSDN: String.Concat 方法 (Object) (System)
  3. MSDN: String.Concat(T) 方法 (IEnumerable(T)) (System)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM