OO世界中的核心概念 .NET中的多態
多態(Polymorphism)一詞源于生物學,顧名思義就是指多種形態。在面向對象世界里,多態與繼承和封裝一起構成了三大核心概念。
.NET中的多態通常意味著子類對于父類一種衍變。子類繼承自父類,擁有父類所定義的一切(public或protected)成員。但同時,它又可以修改(重寫或復寫)這些成員,使其實現與父類以及其他子類完全不同。我們可以說,繼承體現了類的多態性。
大家應該很熟悉Duck的例子了吧?
- public abstract class Duck
- {
- public abstract void Quack();
- }
- public class MallardDuck : Duck
- {
- public override void Quack()
- {
- Console.WriteLine("Quack, quack, quack...");
- }
- }
- public class RubberDuck : Duck
- {
- public override void Quack()
- {
- Console.WriteLine("Squeak, squeak, squeak...");
- }
- }
- public class Program
- {
- public static void Main()
- {
- Duck duck = new MallardDuck();
- duck.Quack();
- duck = new RubberDuck();
- duck.Quack();
- Console.ReadLine();
- }
- }
MallardDuck和RubberDuck雖然都繼承自抽象類Duck,同樣擁有Quack()方法,但它們卻有不同的實現,產生不同的結果。在聲明Duck類型時,既可以實例化為Mallard,也可以實例化為RubberDuck,或者是其他繼承自Duck的類,在運行時,將自動調用各個子類的實現。
多態的這些特性使依賴注入和面向抽象編程成為可能,其重要性不言而喻。
不一樣的多態
然而,既然多態是指同一類事物之間的不同形態,那么我們為什么要把對于多態的理解局限于類的繼承關系呢?在.NET中是否還存在著非繼承關系的多態性呢?
泛型體現了參數的多態性
類型參數在泛型中通常解釋為占位符,而我更愿意將其理解為對參數的一種抽象。以最常見的List< T>為例,List< string>和List< int>在語法上完全相同,僅僅是類型參數有所不同,然而它們卻是兩個完全不同的類。也就是說,是類型參數的不同,導致了不同的類的形態。
- public class MyList< T>
- {
- private T[] items;
- private int size;
- public void Add(T item)
- {
- if (size == items.Length)
- {
- // modify capacity
- }
- items[size++] = item;
- }
- }
如果我們使用MyList< string>,在內部就會聲明一個字符串數組,Add方法的參數也必須為string。如果使用MyList< int>,在內部就會聲明一個int數組,Add方法的參數也必須為int。這看上去就像是T是string和int的“基類”,在使用MyList< T>時(相當于客戶端代碼),T既可以是string也可以是int,或者是其他符合約束的類型,但在設計時,我們對這一切毫無所知。
您是否也覺得這是多態性的一種體現呢?
再來看看十分經典的Swap< T>的例子。
- public class Swapper
- {
- private static void Swap< T>(ref T o1, ref T o2)
- {
- T temp = o1;
- o1 = o2;
- o2 = temp;
- }
- }
Swap< T>泛型方法就像是封裝了N個非泛型的Swap方法,如Swap(ref int o1, ref int o2)、Swap(ref string o1, ref string o2)等等。在類型推斷特性的支持下,您甚至可以像使用非泛型方法一樣來使用泛型方法。參數T在某種程度上體現了不同的參數形態,因此我們有理由認為,泛型類型T體現了參數的多態性。
委托體現了方法的多態性
委托是對擁有相同參數和返回值的所有方法的封裝。只要方法擁有同樣的參數列表和返回值,委托都認為它們屬于同一“類型”的方法,可以添加到同一個委托鏈表中。
- public delegate void FooDelegate(List< string> list, string str);
- public class DelegateTest
- {
- public void AddToList(List< string> list, string strToAdd)
- {
- list.Add(strToAdd);
- }
- public static void PrintIfContains(List< string> list, string strToCheck)
- {
- if (list.Contains(strToCheck))
- Console.WriteLine("The list contains " + strToCheck);
- }
- }
- public class Program
- {
- public static void Main()
- {
- List< string> list = new List< string>();
- list.Add("Kirin");
- DelegateTest delegateTest = new DelegateTest();
- FooDelegate fooDelegate = new FooDelegate(delegateTest.AddToList);
- fooDelegate += new FooDelegate(DelegateTest.PrintIfContains);
- fooDelegate(list, "麒麟.NET");
- Console.ReadLine();
- }
- }
在上例中,FooDelegate委托封裝了參數為List< string>和string,并且沒有返回值的方法。任何符合上述約束的方法,在FooDelegate中一視同仁。如,AddToList實例方法與PrintIfContains靜態方法除了參數列表與返回值相同外,內部實現完全不同,但是它們卻可以添加到同一個委托鏈表中。也就是說,同一個委托,可以定義并調用不同的方法(約束相同而實現不同)。
您是否也認為這是方法的多態性的一種體現呢?
小結
我們通常所討論的多態,就是指子類對父類方法的重寫(虛方法)或覆蓋(非虛方法),這樣的理解未免過于狹隘。.NET強大的特性能夠實現其他語言中無法實現的多態性。
【編輯推薦】