引入泛型方法將讓編譯器對重載的解析變得非常複雜,每個泛型方法的型別參數都可以任意轉換。如果稍有疏忽,程式的行為都將變得極其古怪,在建立泛型類或者方法時,必須保證讓使用者能夠儘可能的理解你的設計意圖,安全的使用你的代碼。
我們來查看以下的代碼,首先定義一個具有繼承層次的結構。
代碼
1 public class MyBase
2 { }
3
4 public interface MyInterface
5 {
6 void WriteMessage();
7 }
8
9 public class MyDerived : MyBase, MyInterface
10 {
11 public void WriteMessage()
12 {
13 Console.WriteLine("MyDerived.WriteMessage");
14 }
15 }
16
17 public class AnotherImpl : MyInterface
18 {
19 public void WriteMessage()
20 {
21 Console.WriteLine("AnotherImpl.WriteMessage");
22 }
23 }
下面是測試代碼。
代碼
1 private static void WriteMessage(MyInterface obj)
2 {
3 Console.WriteLine("Inside WriteMessage(MyInterface)");
4 obj.WriteMessage();
5 }
6
7 private static void WriteMessage(MyBase obj)
8 {
9 Console.WriteLine("Inside WriteMessage(MyBase)");
10 }
11
12 private static void WriteMessage<T>(T obj)
13 {
14 Console.WriteLine("Inside WriteMessge<T>()");
15 Console.WriteLine(obj.ToString());
16 }
17
18 private static void Test()
19 {
20 MyDerived derived = new MyDerived();
21 WriteMessage(derived);
22 Console.WriteLine();
23 WriteMessage((MyInterface)derived);
24 Console.WriteLine();
25 WriteMessage((MyBase)derived);
26 Console.WriteLine();
27
28 AnotherImpl another = new AnotherImpl();
29 WriteMessage(another);
30 Console.WriteLine();
31 WriteMessage((MyInterface)another);
32 }
你能猜出程式的輸出結果嗎?
程式的輸出結果如下所示
其實,下面這行代碼是執行的最詭異的一行代碼
1 MyDerived derived = new MyDerived();
2 WriteMessage(derived);
這說明:對於一個派生於MyBase的對象來說,WriteMessage<T>(T obj)要比WriteMessage(MyBase b)在重載匹配上更加優先。這是因為通過將T替換成MyDerived,編譯器即可完成一個精確的匹配,而WriteMessage(MyBase b)則還需要進行一次隱式類型轉換,因此編譯器在這種情況下選擇了泛型版本的重載形式。
而接下來的兩個測試,則說明即使不在類型的繼承體系中,編譯器選擇重載版本時,也會遵循同樣的原則,即如果參數的運行類型不能夠精確匹配非泛型版本中的參數類型時,那麼編譯器會優先考慮使用泛型版本的重載形式;如果參數的運行時類型能夠精確匹配費泛型版本中的參數類型時,納悶編譯器會優先考慮使用非泛型版本的重載形式。相對於類型轉換來說(這裡指隱式類型轉換),泛型版本更具優勢。
由此,我們可以看出編譯器對名字解析的規則是非常有趣的,當你想支援某一個類型及其衍生類別型時,基於基類建立泛型並不是一個好的選擇,同樣,基於介面也是如此。我們在進行設計時,需要優先考慮讓編譯器自己決定參數的類型,而不是在程式中進行動態判定。
有時,我們也可以為特定的類型建立專門的處理方法,這時需要對重載類型的類型進行檢查,如果符合條件,那麼就會執行特定的邏輯。通常情況下,是不建議這麼做的,因為這樣違反了泛型的初衷,因為泛型就是對類型的抽象。
在一個有繼承關係或者介面實現的情境中,重載方法時需要特別注意,尤其是同時包含了非泛型版本的重載形式和泛型版本的重載形式,你需要非常清楚的知道,在運行時,到底是哪種形式的重載會被調用。