一、對于繼承自同一基類的多個不同派生類的實體,在運行時可以將它們賦值給基型別別的變數,訪問該變數的同一個成員會根據該變數運行時型別的不同而產生不同的行為,這個特性即被稱為多型(Polymorphism);多型允許以同一種方式訪問同一個變數的成員而產生不同的行為;
1.在C#中,每個型別都是多型的,因為包括自定義型別在內的所有型別都繼承自基類System.Object;
2.虛成員、抽象成員和重寫成員、替換成員是多型的基礎;
3.非抽象基類中可以將方法、屬性、事件和索引器宣告為虛成員并實作,抽象基類中可以將這些成員宣告為抽象成員而不實作,派生類中可以通過override關鍵字重寫這些虛成員或抽象成員提供差異化實作,這樣會代替基類中的虛成員,此時無論在代碼中呼叫編譯時型別為基類或派生類的變數的成員,在運行時CLR都會根據變數的運行時型別是基類或派生類來判斷呼叫基類中的原成員或派生類中的重寫成員;
4.如果不想讓基類中的虛成員被代替,則應該在派生類中通過new關鍵字隱藏此虛成員并提供新的實作,此時在代碼中呼叫編譯時型別為基類的變數的成員,在運行時CLR會呼叫基類中該方法的實作;在代碼中呼叫編譯時型別為派生類的變數的成員,在運行時CLR會呼叫派生類中該方法的實作;
public class Animal { public virtual void Call() { Console.WriteLine("call~"); } } public class Cat : Animal { //使用override重寫基類虛方法時,轉換為基類變數后呼叫的為派生類中的重寫方法 public override void Call() { Console.WriteLine("miao~"); } } public class Dog : Animal { //使用new隱藏基類虛方法時,轉換為基類變數后呼叫的為基類中的虛方法 public new void Call() { Console.WriteLine("wang~"); } }
使用方式:
new Cat().Call(); //miao~ new Dog().Call(); //wang~ List<Animal> animals = new List<Animal>() { new Cat(), new Dog() }; //此處使用了隱式轉換,將派生類物件隱式轉換為基類變數 for (int i = 0; i < animals.Count; i++) { animals[i].Call(); //miao~ call~ }
5.通過隱式轉換將派生類實體賦值給基類變數,再將該基類變數參考的物件通過顯式轉換或as運算子轉換后賦值給派生類變數,此時該派生類變數參考的物件的所有資料和行為較最初的派生類實體相比不會發生任何改變,實際上,類的實體在型別轉換程序中其型別不會發生任何改變,只是在呼叫程序中會根據參考該實體的變數的編譯時型別執行不同的呼叫方式,會優先查找實體中變數的編譯時型別所對應的實作;
class BaseClass { public virtual void Method1() { Console.WriteLine("Base_Method1"); } public virtual void Method2() { Console.WriteLine("Base_Method2"); } } class DerivedClass : BaseClass { public int MyNum; public override void Method1() { Console.WriteLine("Derived_Method1"); } public new void Method2() { Console.WriteLine("Derived_Method2"); } }
使用方式:
DerivedClass derivedObj = new DerivedClass() { MyNum = 1 }; BaseClass baseObj = new BaseClass(); Console.WriteLine(derivedObj is BaseClass); //true Console.WriteLine(baseObj is DerivedClass); //false derivedObj.Method1(); //Derived_Method1 derivedObj.Method2(); //Derived_Method2 //將派生類物件隱式轉換為基類物件 baseObj = derivedObj; //此時baseObj中存的依然是原來的DerivedClass的實體,通過反射即可得知 Type myType = baseObj.GetType(); Console.WriteLine(myType.Name); //DerivedClass Console.WriteLine(myType.GetField("MyNum").GetValue(baseObj)); //1 myType.GetMethod("Method1").Invoke(baseObj, null); //Derived_Method1 myType.GetMethod("Method2").Invoke(baseObj, null); //Derived_Method2,通過反射獲取方法資訊陣列時會發現有兩個Method2,但這么呼叫只會呼叫派生類中的Method2 //通過baseObj直接訪問 Console.WriteLine(baseObj is DerivedClass); //true baseObj.Method1(); //Derived_Method1 baseObj.Method2(); //Base_Method2 //將上面轉換后的基類變數參考的物件再次轉換為派生類物件,其原始資料不變 DerivedClass derivedObj2 = baseObj as DerivedClass; derivedObj2.Method1(); //Derived_Method1 derivedObj2.Method2(); //Derived_Method2 Console.WriteLine(derivedObj2.MyNum); //1
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!
作者:Minotauros
出處:https://www.cnblogs.com/minotauros/
本文著作權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/112658.html
標籤:C#
上一篇:C#面向物件--繼承
下一篇:裝箱與拆箱的“把戲”
