面向對象-多態
多態
通過繼承,一個類型可以用作多種類型,可以用作自己的類型,任何基類類型,或者在實現接口時用作任何接口類型稱多態。
在C#中所有引用類型自動將 System.Object 類型作為基類,值類型自動將 System.ValueType 類型作為基類。
代碼:
1 /// <summary> 2 /// 基類接口,定義行為。 3 /// </summary> 4 public interface IPeople 5 { 6 void Walk(); 7 } 8 9 /// <summary>10 /// 基類 11 /// </summary> 12 public class People: IPeople 13 { 14 public int _age { get; set; } 15 public string _name { get; set; } 16 public People(string name, int age = 1) 17 { 18 _age = age; 19 _name = name; 20 } 21public void Walk() 22 { 23 Console.WriteLine($"{_name} will walk."); 24 } 25 26 public void Introduction() 27 { 28 Console.WriteLine($" name:{_name} age:{_age} Introduction."); 29 } 30 31 public void Specialty() 32 {33 Console.WriteLine($" name:{_name} age:{_age} Specialty."); 34 } 35 } 36 37 /// <summary> 38 /// 派生自People 39 /// </summary> 40 public class Man : People 41 { 42 public Man(string _name, int _age) : base(_name, _age) 43 { 44 } 45 } 46 47 /// <summary> 48 /// 派生自People 49 /// </summary> 50 public class Woman : People 51 { 52 public Woman(string _name, int _age) : base(_name, _age) 53 { 54 } 55 }
1 static void Main(string[] args) 2 { 3 People xiaoming = new Man("xiaoming",20); 4 xiaoming.Walk(); 5 6 People xiaohong = new Woman("xiaohong", 19); 7 xiaohong.Walk(); 8 9 IPeople xiaowang = new Woman("xiaowang", 19); 10 xiaohong.Walk(); 11 }
上述代碼中 People 類既能用作於 Man 類和 Woman 類,People類繼承實現 IPeople 接口,IPeople 也可用作 Man 類和 Woman 類。多態不僅對派生類很重要,對基類也很重要。使用基類實際上都可能是在使用已強制轉換為基類類型的派生類對象。基類的設計者可能預測到其基類中可能會在派生類中發生更改的地方。
多態概念:
當派生類從基類繼承時,它會獲取基類的所有方法,字段,屬性和事件面向對象語言使用虛方法來表示多態。若要改變基類數據和行為。你有兩種方法選擇:可以使用新的派生成員替換基類成員,或者重寫虛方法。
使用新的派生成員替換基類成員需要使用 new 關鍵字。如果基類定義了一個方法,字段或者屬性,則 new 關鍵字用於在派生類中創建該方法,字段和屬性的新定義。new 關鍵字應該放在替換成員的返回類型的前面。
代碼:
1 /// <summary> 2 /// 基類 3 /// </summary> 4 public class People : IPeople 5 { 6 public virtual int _age { get; set; } 7 public virtual string _name { get; set; } 8 public People(string name, int age = 1) 9 { 10 _age = age; 11 _name = name; 12 } 13 public virtual void Walk() 14 { 15 Console.WriteLine($"{_name} will walk."); 16 } 17 } 18 19 /// <summary> 20 /// 派生自People 21 /// </summary> 22 public class Man : People 23 { 24 public new int _age { get; set; } 25 public new string _name { get; set; } 26 27 public Man(string _name, int _age) : base(_name, _age) 28 { 29 } 30 31 public new void Walk() 32 { 33 Console.WriteLine($"{_name} will walk."); 34 } 35 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Man man = new Man("xiaoming", 20); 6 man.Walk(); 7 } 8 }
派生類使用 new 關鍵字對基類的成員重寫定義,調用的是派生類的新成員而不是基類的成員,此時的基類成員已經被派生類的新成員替代了。此時基類成員稱為隱藏成員。這裏需要註意的是如果將派生類型實例強制轉換為基類的實例,就仍然調用的基類的成員。
代碼:
1 static void Main(string[] args) 2 { 3 Man man = new Man("xiaoming", 20); 4 man.Walk();// 調用的新方法 5 6 People people = (People)man; 7 people.Walk();// 調用的舊方法 8 }
為了派生類的實例完全替換掉基類的成員,基類必須將該成員聲明為虛擬的。聲明虛擬的成員時在成員的返回類型前面添加 virtual 關鍵字而不是 new 關鍵字。
代碼:
1 /// <summary> 2 /// 基類 3 /// </summary> 4 public class People : IPeople 5 { 6 public virtual int _age { get; set; } 7 public virtual string _name { get; set; } 8 public People(string name, int age = 1) 9 { 10 _age = age; 11 _name = name; 12 } 13 public virtual void Walk() 14 { 15 Console.WriteLine($"{_name} will walk."); 16 } 17 } 18 19 /// <summary> 20 /// 派生自People 21 /// </summary> 22 public class Man : People 23 { 24 public override int _age { get; set; } 25 public override string _name { get; set; } 26 public Man(string _name, int _age) : base(_name, _age) 27 { 28 } 29 30 public override void Walk() 31 { 32 Console.WriteLine($"{_name} will walk."); 33 } 34 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Man man = new Man("xiaoming", 20); 6 man.Walk();// 調用的新方法 7 8 People people = (People)man; 9 people.Walk();// 調用的新方法 10 11 } 12 }
需要註意的是字段是不能為虛擬的,只有方法,屬性,事件和索引器可以是虛擬的。當派生類中重寫基類成員後時,即使派生類實例強制轉換為基類的實例時,調用的還時派生類的新成員。
使用虛擬方法和屬性可以預先計劃未來的擴展。由於在調用虛擬成員時不考慮調用方正在使用的類型,所以派生類可以選擇完全更改基類的外觀行為。無論在派生類和最初聲明虛擬成員的類之間已聲明了多少個類,虛擬成員都將永遠為虛擬成員。如果類 A 聲明了一個虛擬成員,類 B 從 A 派生,類 C 從類 B 派生,則類 C 繼承該虛擬成員,並且可以選擇重寫它,而不管類 B 是否為該成員聲明了重寫。
代碼:
1
派生類可以通過將重寫聲明為密封的來停止虛擬繼承。這需要在類成員聲明中將 sealed 關鍵字放在 override 關鍵字的前面
代碼:
1 /// <summary> 2 /// 基類 3 /// </summary> 4 public class People : IPeople 5 { 6 public virtual int _age { get; set; } 7 public virtual string _name { get; set; } 8 public People(string name, int age = 1) 9 { 10 _age = age; 11 _name = name; 12 } 13 public virtual void Walk() 14 { 15 Console.WriteLine($"{_name} will walk."); 16 } 17 } 18 19 /// <summary> 20 /// 派生自People 21 /// </summary> 22 public class Man : People 23 { 24 public override int _age { get; set; } 25 public override string _name { get; set; } 26 public Man(string _name, int _age) : base(_name, _age) 27 { 28 } 29 30 public override void Walk() 31 { 32 Console.WriteLine($"{_name} will walk."); 33 } 34 } 35 36 public class ChinesePeople : Man 37 { 38 public override int _age { get; set; } 39 public override string _name { get; set; } 40 public ChinesePeople(string _name, int _age) : base(_name, _age) 41 { 42 } 43 44 public override void Walk() 45 { 46 Console.WriteLine($"{_name} will walk."); 47 } 48 }
派生類可以通過將重寫聲明為密封的來停止虛擬繼承。這需要在類成員聲明中將 sealed 關鍵字放在 override 關鍵字的前面
代碼:
1 public class ChinesePeople : Man 2 { 3 public sealed override int _age { get; set; } 4 public sealed override string _name { get; set; } 5 public ChinesePeople(string _name, int _age) : base(_name, _age) 6 { 7 } 8 9 public sealed override void Walk() 10 { 11 Console.WriteLine($"{_name} will walk."); 12 } 13 }
在上面的代碼中,方法 Walk 從 ChinesePeople 派生的任何類都不再是虛擬的。它對 ChinesePeople 的實例仍然是虛擬的 ,即使將這些實例強制轉換為類型 Man 或類型 People 。派生類可以通過使用 new 關鍵字替換密封的方法。
代碼:
1 public class SZPeople : ChinesePeople 2 { 3 public new int _age { get; set; } 4 public new string _name { get; set; } 5 public SZPeople(string _name, int _age) : base(_name, _age) 6 { 7 } 8 9 public new void Walk() 10 { 11 Console.WriteLine($"{_name} will walk."); 12 } 13 }
在此情況下,如果在 SZPeople 中使用類型為 SZPeople 的變量調用 Walk ,被調用的是新的 Walk ,也就是 SZPeople的新成員。如果使用類型為 ChinesePeople、Man 或 People 的變量訪問 ChinesePeople 的實例,對 Walk 的調用將遵循虛擬繼承的規則,即把這些調用傳送到類 Man 的 Walk 實現。已替換或重寫某個方法或屬性的派生類仍然可以使用基關鍵字訪問基類的該方法或屬性。
註意建議虛擬成員在它們自己的實現中使用 base 來調用該成員的基類實現。允許基類行為發生使得派生類能夠集中精力實現特定於派生類的行為。未調用基類實現時,由派生類負責使它們的行為與基類的行為兼容。
源碼
面向對象-多態