前言
在物件導向程式設計中,繼承 (Inheritance) 是一個非常核心的概念。透過繼承,我們可以讓一個類別 (稱作子類別或衍生類別 Derived Class) 從另一個類別 (稱作父類別或基底類別 Base Class) 延伸 (inherit) 出去,擁有基底類別的可繼承的欄位、屬性與方法,並且又可以有自己獨特的欄位、屬性與方法。這可以提高程式的重複使用性,也讓系統架構更有彈性與可擴充性。
語法
使用 :
繼承。僅支援單一繼承,也就是只能繼承一個類別。但是可以有繼承階層,例如:C
繼承自 B
,而 B
繼承自 A
,因此 C
可以使用 A
的欄位與方法。也可以基底類別被不同的衍生類別繼承,例如:C
和 B
都繼承自 A
。
繼承階層
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class 基底類別 { }
class 衍生類別1 : 基底類別 { }
class 衍生類別2 : 衍生類別1 { }
|
範例:
HarryPotter1
類別透過 : Movie
繼承 Movie
類別。
HarryPotter1
可以使用 Movie
中的 Play()
方法。
HarryPotter2
繼承 HarryPotter1
類別。
HarryPotter2
可以使用 Movie
中的 Play()
方法和 HarryPotter1
中的 CastBasicSpell()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Movie { public void Play() { Console.WriteLine("播放電影..."); } }
class HarryPotter1 : Movie { public void CastBasicSpell() { Console.WriteLine("施展基本魔法..."); } }
class HarryPotter2 : HarryPotter1 { public void CastTransfigurationSpell() { Console.WriteLine("施展變形咒..."); } }
|
基底類別被不同的衍生類別繼承
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class 基底類別 { }
class 衍生類別1 : 基底類別 { }
class 衍生類別2 : 基底類別 { }
|
範例:
HarryPotter
可以使用 Movie
中的 Play()
方法,和自己的 CastBasicSpell()
方法。
SpiderMan
可以使用 Movie
中的 Play()
方法,和自己的 ShootWebs()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Movie { public void Play() { Console.WriteLine("播放電影..."); } }
class HarryPotter : Movie { public void CastBasicSpell() { Console.WriteLine("施展基本魔法..."); } }
class SpiderMan : Movie { public void ShootWebs() { Console.WriteLine("射出蜘蛛網..."); } }
|
Protected 欄位與方法
之前有提過封裝與存取修飾詞,而修飾詞 protected
表示只有同一個類別內或其衍生類別可以存取,資料在基底類別與衍生類別之間共享。protected
介於 private
和 public
之間,提升封裝性又保留靈活性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Movie { protected string _title; private string _privateNote;
public Movie() { this._title = "電影名稱"; this._privateNote = "這是基底類別"; } }
class HarryPotter : Movie { public HarryPotter() { this._title = "哈利波特"; }
public void DisplayTitle() { Console.WriteLine($"Title: {_title}"); } }
|

關鍵字 base
base
表示基底類別本身,用來呼叫基底類別 (base class) 中的建構子、欄位、屬性或方法。
如果基底類別的建構式有參數 (像是:line 5 - line 8),則衍生類別需要明確標示建構式的繼承,例:line 18。
在衍生類別中想要呼叫基底類別的方法,例:line 24。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class Movie { protected string _title;
public Movie(string title) { this._title = title; } public void ShowInfo() { Console.WriteLine("電影資訊"); } }
class HarryPotter : Movie { public HarryPotter(string title) : base(title) { }
public void ShowMagic() { base.ShowInfo(); Console.WriteLine("霍格華茲魔法學校"); } }
class Program { public static void Main(string[] args) { HarryPotter m = new HarryPotter("哈利波特1"); m.ShowMagic(); } }
|
如果基底類別的建構式沒有參數 (像是:line 5 - line 8),則衍生類別不需要明確標示建構式的繼承,例:line 13 - line 16。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Movie { protected string _title;
public Movie() { this._title = "電影名稱"; } }
class HarryPotter : Movie { public HarryPotter() { this._title = "哈利波特"; } }
|
利用 virtual
+ override
覆寫基底類別的方法
若基底類別的方法使用 virtual
修飾詞,衍生類別就可以用 override
進行覆寫、修改邏輯,而不同的衍生類別就可以有自己客製化的邏輯。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class Movie { protected string _title;
public Movie(string title) { this._title = title; } public virtual void ShowGenre() { Console.WriteLine($"{_title} 是一般電影類別"); } }
class HarryPotter : Movie { public HarryPotter(string title) : base(title) { } public override void ShowGenre() { Console.WriteLine($"{_title} 是奇幻冒險"); } }
class Program { public static void Main(string[] args) { HarryPotter m = new HarryPotter("哈利波特1"); m.ShowGenre(); } }
|
抽象類別 (Abstract Class) 與抽象方法 (Abstract Method)
當基底類別提供設計架構而不提供實作時,可以使用 abstract
類別與方法。目的:強迫衍生類別一定要定義該方法的內容。
abstract class
無法被實體化,只能被繼承。
abstract method
沒有定義內容,一定要放在 abstract class
中,且必須在子類別中用 override
覆寫並實作內容。
1 2 3 4 5 6 7 8 9 10 11 12
| abstract class Movie { public abstract void PlayTheme(); }
class HarryPotter : Movie { public override void PlayTheme() { Console.WriteLine("Playing Hedwig's Theme..."); } }
|