測試程序
我們來看看下面這個非常簡單的 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>)
可以看出,總共只有三處不同:
- 第3行的不同說明了不同的 .NET Framework 版本。
- 第15行的不同只是說明了不同的 Tester.exe 的 GUID 不同。
- 第45行的不同說明了調用的 Concat 方法的重載版本不同。
上述第3點不同正好驗證了我們在上一節中的分析,明確顯示出了相應調用的 Concat 方法的不同的重載版本。
參考資料
- MSDN: String.Concat 方法 (System)
- MSDN: String.Concat 方法 (Object) (System)
- MSDN: String.Concat(T) 方法 (IEnumerable(T)) (System)