在 Eric Lippert 的部落格看到一段挺有意思的 C# code,以下是我稍微修改過的版本:
當主程式呼叫 C.DoIt<string>("Mike") 時,C.DoIt 方法所呼叫的 ReallyDoIt 是哪個版本?
執行結果會輸出 "Generic version: Mike",也就是說,呼叫的是泛型(generic)的版本。
原因在於,當編譯器碰到 overloaded methods 時,會根據呼叫時所傳遞的參數型別來決定哪一個 method 才是最速配(match)的版本。在這個例子當中,由於呼叫時傳入的參數型別是泛型 T,於是編譯器會尋找同樣需要傳入泛型參數的 ReallyDoIt 方法。
如果改為直接呼叫 C.ReallyDoIt("Mike"),那麼執行的結果就會是 "Natural version: Mike"。
上面的解釋聽起來很合理,似乎沒什麼特別,但作者卻點出一個我們可能忽略的技術細節。寫過 C++ 的人,可能會以「泛型其實就是樣板(template)型別」來理解和解釋泛型,但從上面的例子可以發現,C# 的泛型和 C++ 的樣板其實骨子裡是不同的。
C++ 編譯器在處理樣板類別時,會根據實際需要的參數型別產生多種版本的類別代碼,也就是說,實際編譯出來的 code 全都是可直接繫結的真實型別,根本沒有樣板這東西了。但 C# 的泛型--如 Lippert 所言--就只是泛型;編譯器並不會因為你在呼叫泛型 method 時分別用到了 string 和 int 兩種參數而編譯出兩種版本的 method 代碼。觀察反組譯出來的 IL code 便可確認這點(僅列出宣告部分):
class Program
{
static void Main(string[] args)
{
C.DoIt<string>("Mike");
}
}
public class C
{
public static void DoIt<T>(T t)
{
ReallyDoIt(t);
}
public static void ReallyDoIt(string s)
{
System.Console.WriteLine("Natural version: " + s);
}
public static void ReallyDoIt<T>(T t)
{
System.Console.WriteLine("Generic version: " + t);
}
}當主程式呼叫 C.DoIt<string>("Mike") 時,C.DoIt 方法所呼叫的 ReallyDoIt 是哪個版本?
執行結果會輸出 "Generic version: Mike",也就是說,呼叫的是泛型(generic)的版本。
原因在於,當編譯器碰到 overloaded methods 時,會根據呼叫時所傳遞的參數型別來決定哪一個 method 才是最速配(match)的版本。在這個例子當中,由於呼叫時傳入的參數型別是泛型 T,於是編譯器會尋找同樣需要傳入泛型參數的 ReallyDoIt 方法。
如果改為直接呼叫 C.ReallyDoIt("Mike"),那麼執行的結果就會是 "Natural version: Mike"。
上面的解釋聽起來很合理,似乎沒什麼特別,但作者卻點出一個我們可能忽略的技術細節。寫過 C++ 的人,可能會以「泛型其實就是樣板(template)型別」來理解和解釋泛型,但從上面的例子可以發現,C# 的泛型和 C++ 的樣板其實骨子裡是不同的。
C++ 編譯器在處理樣板類別時,會根據實際需要的參數型別產生多種版本的類別代碼,也就是說,實際編譯出來的 code 全都是可直接繫結的真實型別,根本沒有樣板這東西了。但 C# 的泛型--如 Lippert 所言--就只是泛型;編譯器並不會因為你在呼叫泛型 method 時分別用到了 string 和 int 兩種參數而編譯出兩種版本的 method 代碼。觀察反組譯出來的 IL code 便可確認這點(僅列出宣告部分):
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
}
.method public hidebysig static void DoIt(!!T t) cil managed
{
}
.method public hidebysig static void ReallyDoIt(!!T t) cil managed
{
}
.method public hidebysig static void ReallyDoIt(string s) cil managed
{
}
}
Good article!
回覆刪除