一、多態 多態是從繼承中引出來的概念,即不同的派生類對基類的相同方法表現出不同的行為。如下麵例子中動物的游泳方法: 當用戶使用派生類雞的游泳方法時,由於基類的游泳方法滿足雞的需求,則直接調用基類的游泳方法返回不會;當用戶使用派生類狗和蛙的游泳方法時,由於基類的游泳方法的實現不滿足狗和蛙的需求,所以狗 ...
一、多態
多態是從繼承中引出來的概念,即不同的派生類對基類的相同方法表現出不同的行為。如下麵例子中動物的游泳方法:
1 /// <summary> 2 /// 動物 3 /// </summary> 4 public class Animal 5 { 6 public string Swimming() 7 { 8 return "不會"; 9 } 10 } 11 /// <summary> 12 /// 雞 13 /// </summary> 14 public class Chicken : Animal 15 { 16 17 } 18 /// <summary> 19 /// 狗 20 /// </summary> 21 public class Dog : Animal 22 { 23 } 24 /// <summary> 25 /// 蛙 26 /// </summary> 27 public class Frog : Animal 28 { 29 }
當用戶使用派生類雞的游泳方法時,由於基類的游泳方法滿足雞的需求,則直接調用基類的游泳方法返回不會;當用戶使用派生類狗和蛙的游泳方法時,由於基類的游泳方法的實現不滿足狗和蛙的需求,所以狗和蛙需要實現自己的邏輯。如何實現狗的狗刨和蛙的蛙泳?答案是重寫(使用關鍵字virtual和override在派生類中重新實現基類的同名方法)。
1 public class Animal 2 { 3 //設計基類時對需要在派生類中重寫的方法使用virtual修飾,使用virtual修飾的方法叫做虛方法 4 public virtual string Swimming() 5 { 6 return "不會"; 7 } 8 } 9 public class Dog : Animal 10 { 11 //派生類若要重寫基類的方法,可使用override修飾與基類同名的方法,實現自己的行為,被override修飾的方法也可被重寫 12 public override string Swimming() 13 { 14 return "狗刨"; 15 } 16 } 17 public class Frog : Animal 18 { 19 //派生類若要重寫基類的方法,可使用override修飾與基類同名的方法,實現自己的行為,被override修飾的方法也可被重寫 20 public override string Swimming() 21 { 22 return "蛙泳"; 23 } 24 }
上面的例子中,不同的派生類(雞、狗、蛙)對基類(動物)的游泳方法表現出各自不同的結果,即類的多態特性。
也可以使用另一種方式遮擋來實現類的多態特性,即派生類使用new關鍵字來實現與基類同名方法的不同行為。
1 public class Animal 2 { 3 public string Swimming() 4 { 5 return "不會"; 6 } 7 } 8 public class Dog : Animal 9 { 10 //使用new關鍵字遮擋基類的同名方法,也可以使用到派生類的其他成員中 11 public new string Swimming() 12 { 13 return "狗刨"; 14 } 15 } 16 public class Frog : Animal 17 { 18 //使用new關鍵字遮擋基類的同名方法,也可以使用到派生類的其他成員中 19 public new string Swimming() 20 { 21 return "蛙泳"; 22 } 23 }
註:主要使用的情況是當我們沒有修改基類的許可權又希望實現派生類的多態特性時。
二、C#關鍵字:base
在派生類的方法中使用“base.基類方法名”可以復用基類的方法。
三、C#關鍵字:sealed
由於被override修飾的方法是隱式可重寫的,所以當我們不希望被override修飾的方法被重寫時,可以使用sealed關鍵字防止被重寫。
1 public class Dog : Animal 2 { 3 //Dog類不希望自己的游泳方法被它的派生類重寫 4 public sealed override string Swimming() 5 { 6 return "狗刨"; 7 } 8 }
註:在防止被重寫中,sealed關鍵字必須與override關鍵字相同存在。
四、抽象類和抽象方法
當一個基類的作用只是為派生類提供公共成員,而無其他實際意義時,我們不希望用戶通過new關鍵字創建這個基類,可將基類設計成抽象類,這樣用戶就不能用new關鍵字創建它。使用abstract關鍵字修飾類可使其變成抽象類。
當抽象類的某個方法在派生類中表現出多態性時,這個方法的實現對派生來說是無用的,我們希望所有派生類必須重寫基類的這個方法,可將這個方法設計成抽象方法,這樣抽象類的抽象方法不用提供預設實現並且派生類必須使用override重寫此抽象方法。如果派生類不重寫這個抽象方法自身也將成為一個抽象類。使用abstract關鍵字修飾方法可使其變成抽象方法。
1 /// <summary> 2 /// 可調度溫度的電子設備 3 /// </summary> 4 public abstract class TemperatureElectric 5 { 6 protected int temperature; 7 8 public abstract int Up(); 9 10 public abstract int Down(); 11 } 12 /// <summary> 13 /// 空調 14 /// </summary> 15 public class AirConditioner : TemperatureElectric 16 { 17 public override int Up() 18 { 19 if (temperature < 30) 20 temperature += 1; 21 return temperature; 22 } 23 24 public override int Down() 25 { 26 if (temperature > 16) 27 temperature -= 1; 28 return temperature; 29 } 30 } 31 /// <summary> 32 /// 冰箱 33 /// </summary> 34 public class Refrigerator : TemperatureElectric 35 { 36 /// <summary> 37 /// 提升冷藏溫度 38 /// </summary> 39 public override int Up() 40 { 41 if (temperature < 7) 42 temperature += 1; 43 return temperature; 44 } 45 46 /// <summary> 47 /// 降低冷藏溫度 48 /// </summary> 49 public override int Down() 50 { 51 if (temperature > 3) 52 temperature -= 1; 53 return temperature; 54 } 55 }
五、幾種概念的對比
1、重載與重寫
重載和重寫的相同點在於他們都是對同名的方法使用。
重載和重寫的不同點:在使用範圍上,前者使用在單個類中,後者使用在兩個或多個有繼承關係的類中;在使用意圖上,前者是寫法上的簡化,後者是功能上的擴展。
2、重寫與遮擋
重寫與遮擋的相同點在於他們都是對基類同名方法的擴展。
重寫與遮擋的不同點在於,通過A a = new B();B繼承於A,前者調用B的同名方法,而後者調用的是A的同名方法;前者用於主動設計,後者用於被動修改。
1 public class RealizeObject 2 { 3 public void Realize() 4 { 5 //Dog是通過virtual重寫的Animal 6 Animal animal = new Dog(); 7 animal.Swimming();//輸出的狗刨 8 9 //Dog是通過new遮擋的Animal 10 Animal animal = new Dog(); 11 animal.Swimming();//輸出的不會 12 } 13 }
3、虛方法和抽象方法
虛方法與抽象方法的相同點在於他們都可以被重寫。
虛方法與抽象方法的不同點:在使用範圍上前者大於後者,前者使用在非密封類中,後者只能使用在抽象類中;在使用方式上,前者必須有實現部分,後者不能有實現部分;在派生類中,前者可重寫可不重寫,後者必須重寫。
4、抽象類與介面類型的區別(見C#介面類型)