物件的多重身份- 繼承與多型

111
物物物物物物物物物物物物

description

物件的多重身份- 繼承與多型. 7.1 繼承- Inheritance. 類別與類別之間是可以繼承的,繼承的目的除了讓新開發的類別得以延用即有類別的功能外,更深一層的目的就是表示類別的結構。. 7.1 繼承- Inheritance. 將軍需能夠掃射的重坦克!如何快速的改裝新的重坦克呢?! - PowerPoint PPT Presentation

Transcript of 物件的多重身份- 繼承與多型

Page 1: 物件的多重身份- 繼承與多型

物件的多重身份-繼承與多型

Page 2: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

• 類別與類別之間是可以繼承的,繼承的目的除了讓新開發的類別得以延用即有類別的功能外,更深一層的目的就是表示類別的結構。

Page 3: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

將軍需能夠掃射的重坦克!如何快速的改裝新的重坦克呢?!

• 定義坦克( Tank)類別的目的就是要描述什麼是坦克,而哪些資訊與行為是這個坦克所擁有的(即是欄位、特性與方法)。現在將軍需要一台重坦克( HeavyTank),上面有一座機槍可以進行掃射,此時只要將原本的坦克改裝一下,上面裝座機槍,這樣就變成將軍要的重坦克了,如此就可以進行掃射的能力了。重坦克除了多了一座機槍( MachineGun),並可進行掃射( Rake())外,其實重坦克就像一般的坦克一樣,同樣會有編號( ID)與健康值( HP)等特性,當然也會有射擊方法( Fire)以射擊坦克。為了讓重坦克擁有坦克的資訊與行為,不就要把坦克類別中的程式碼拷貝並重貼到新的重坦克類別中嗎?但更糟的是若沒有原始碼呢?那不就拷貝不到坦克類別中的程式。

Page 4: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_1 HeaveryTank.cs

class HeaveyTank : Tank{}

重坦克的英文是 HeaveyTank 。冒號( : )讓重坦克( HeaveyTank )繼承自坦克( Tank )。

冒號( : )後面接的就是要被重坦克繼承的坦克類別。重坦克也是類別,因此也會有一

對大括號( { } )讓重坦克類別可以裝欄位、特性與方法。

• 這樣就完成了繼承的關係。嗯?!忘了坦克類 別長什麼樣子了嗎?沒關係,我們目前所使用的坦克類別就列在下方:

Page 5: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_1 Tank.cs

class Tank{private string id;private int hp = 1000;public String ID{get{return id;}set{id=value;}}public int HP{get{ return hp;}private set{if(value < 0)hp = 0;elsehp = value;}}

健康值欄位( hp )的預設值為 1000 。如此每台坦克的健康值即是 1000 。

這是編號特性( HP )。

這是健康值特性( HP )。

健康值特性( HP )的在設值存取子中加上了健康值最小為0的限制。

Page 6: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance}public void Show(){Console.WriteLine(" 編號: "+ id);Console.WriteLine(" 健康值: "+ hp);}public void Fire(Tank target){Console.WriteLine(" 射擊 ~~~");Bomb b = new Bomb();target.BeHit(b);}public void BeHit(Bomb b){HP = HP - b.Firepower;}}

顯示坦克狀態的方法-顯示狀態方法( Show() )。

這是坦克的射擊方法( Fire() )。

射擊方法改成射出一個飛彈至目標紙上。模擬的方式就是產生一個飛彈,並轉給目標的被擊中( BeHit() )方法,表示飛彈以擊中目標坦克。被擊中方法( BeHit() ),以模擬坦克被飛

彈擊中的狀況。

Page 7: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_1 Bomb.cs

class Bomb{public int Firepower{get{return 300;}}}

Bomb 就是飛彈類別。它只有一個火力特性( Firepower )以表示這顆飛彈的威力。

固定每顆飛彈的威力就是 300 。

Page 8: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

繼承讓重坦克擁有坦克的欄位與方法。• 讓重坦克( HeavyTank )繼承坦克( Tank )的目

的,就是讓重坦克類別直接擁有坦克類別即有的功能,像是編號特性( ID )、健康值特性( HP ),與顯示狀態方法( Show() )和射擊方法( Fire() )都可以一次性的擁有下來。現在我們來測試一下重坦克從類別繼承下來的特性與方法。

Page 9: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_1 Program.cs

class Program{static void Main(string[] args){HeaveyTank h1 = new HeaveyTank();h1.ID = "H001";HeaveyTank h2 = new HeaveyTank();h2.ID = "H002";h1.Show();h1.Fire(h2);Console.WriteLine(h2.ID);Console.WriteLine(h2.HP);}}

建立重坦克物件( HeavyTank )

這些特性與方法並沒有在重坦克類別中描述,但因為重坦克繼承自坦克( Tank )的關係,所以坦克所有的能力都將會被繼承下來。

Page 10: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

重坦克繼承坦克,即產生子類別與父類別的關係。• 重坦克( HeavyTank )因為繼承的關係,可以直

接得到坦克( Tank )的所有特性與方法。而在繼承的關係下,又可分為繼承者與被繼承者的關係;繼承者-重坦克稱為子類別( Sub class ),而被繼承者-坦克稱為父類別( Super class )。這聽起來蠻合理的,就像兒子繼承父親的能力,父親將能力傳給兒子是相同的感覺。在本頁,同時也將介紹統一塑模語言- UML( Unified Modeling Language )來描述類別的結構與關係,而 UML是廣泛應用在業界,以圖形描述物件導向程式的一種語言。

Page 11: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

class HeavyTank : Tank{}

繼承者-重坦克( HeavyTank ),可稱為是子類別。

被繼承者-坦克( Tank ),可稱為是父類別。Tank

ID

HP

Show()

Fire()

BeHit()

HeavyTank

HasMachineGun

Rake()

Show()

最上方是類別的名稱

這是 UML( Unified Modeling Language ,一種業界廣為應用的物件導向程式的圖形描述)用來表示類別的方式。主要是以方形來表示類別。其中可分為三個區塊,由上而下分別為:類別名稱、特性與方法。

第 2 格是用來表示此類別所擁有的特性(註:因 C# 基本上不以欄位對外開放存取,因此在這所指的即是特性)。

第 3 格是用來表示此類別所擁有的方法。

這個單箭頭的連結線表示繼承的意思。有箭頭的那一端連結著父類別,而無箭頭的則是連結子類別。

因為單箭頭連結線表示重坦克類別( Heavy Tank )將繼承自坦克( Tank )類別,同時亦可稱重坦克類別為子類別。

Page 12: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

重坦克可以安裝機槍,並提供掃射的功能嗎?• 重坦克( HeavyTank )成功的以繼承的方式,將

坦克( Tank )所有的特性與方法繼承下來,嗯,這是很理想的。但若想要改良重坦克,讓重坦克可以安裝機槍( Has Machine Gun ),並進行掃射( Rake() )的功能,是不是在繼承之後還是可以加上一些專屬於重坦克的東西呢?!

Page 13: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

我們也可以為重坦克加上「是否安裝機槍特性」與「掃射方法」。

• 重坦克( HeavyTank )除了繼承了坦克( Tank )的欄位、特性與方法外,也應該含有重坦克專屬的資訊與行為。雖然重坦克繼承自坦克,但重坦克還是有一些專屬的資訊與行為;如重坦克的「是否安裝機槍」( HasMachineGun )的資訊,還有若有安裝機槍,就可以進行掃射( Rake() )的行為。現在讓我們來為重坦克加入專屬的資訊與行為:

Page 14: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_2 HeavyTank.cs

class HeavyTank : Tank{private bool hasMachineGun;public bool HasMachineGun{get{return hasMachineGun;}set{ hasMachineGun=value;}}public void Rake(){if(hasMachineGun == true)Console.WriteLine(" 掃射 ~~~~");elseConsole.WriteLine(" 機槍還沒安裝 ...");}}

為重坦克( HeavyTank )加上是否安裝機槍( hasMachineGun )的欄位。提供是否安裝機槍特性( HasMachineGun )來存取是否安裝機槍欄位,並達到封裝的效果。

Rake() 方法是代表重坦克掃射行為的方法。當有安裝機槍的時候(也就是hasMachineGun等於 true ),則進行掃射。

這是未安裝機槍的時候(也就是 hasMachineGun等於false )顯示的訊息。

Tank

ID

HP

Show()

Fire()

BeHit()

HeavyTank

HasMachineGun

Rake()

只要直接在重坦克類別中宣告欄位、特性與方法,這樣就可以為重坦克設定專屬的資訊與行為。

Page 15: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

測試一下重坦克專屬的是否安裝機槍特性與掃射方法。

• 嗯?!好像少測試欄位了,你沒有看錯這個標題,我們的欄位都是私有化的,唯有坦克物件才可以使用,所以我們將忽略坦克的欄位測試。現在就讓我們來測試重坦克專有的特性與方法:

Page 16: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_2 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();h.ID = "H001";h.Show();h.HasMachineGun = true;h.Rake();}}

繼承自坦克類別( Tank )的特性與方法還是可以使用的。

新宣告的特性與方法,在重坦克物件( HeavyTank )上是可以使用的。執行結果:

編號: H001健康值: 1000 掃射

Page 17: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

注意!加在重坦克上的欄位、特性與方法,是不會加在坦克上的。

• 我們為重坦克( HeavyTank )加上的欄位、特性與方法是專屬於重坦克的,並不會同時附加在父類別 - 坦克( Tank )上。現在就讓我們作個錯誤的示範:

Page 18: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_3 Program.cs

class Program{static void Main(string[] args){Tank t= new Tank();t.ID = "T001";t.Show();t.HasMachineGun =true;t.Rake();}}

建立的是坦克物件( Tank ),而不是重坦克的物件( HeavyTank )。父類別-坦克原有的特性與方法,並不

會因為子類別-重坦克加上新的特性與方法,而有所改變。千萬別在坦克物件上使用重坦克的特性

與方法。這是行不通的,這些特性與方法是專屬於重坦克的。若是執意這麼作,編譯器會發生錯誤,告訴您坦克型別( Tank )的物件並不包含是否安裝機槍特性( HasMachineGun )與掃射方法( Rake )

錯誤訊息列會顯示出錯誤訊息:

Page 19: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance

注意!坦克的私有化欄位,並不會因繼承而被重坦克發現。

• 坦克類別上的所有欄位,其實皆已經使用 private關鍵字進行私有化,私有化後的欄位是不可以被繼承的。這個就好像一件密秘是會永遠埋藏在心中,即使是親如兒子也是一樣不被告知。因此若使用重坦克類別( HeavyTank )強行的存取坦克( Tank )私有化的欄位時,就會發生編譯錯誤:

Page 20: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.1 繼承- Inheritance• C7_1_4 Program.cs

class HeavyTank : Tank{//略過一些程式public void TryToGetSecret(){Console.WriteLine(id);Console.WriteLine(hp);}}

刻意在重坦克類別( HeavyTank )上加上一個方法,企圖直接取得坦克類別( Tank )所定義的編號( id )與健康值( hp )欄位中的資料。繼承並不會同時將私有化的欄位也繼承

下來。私有於坦克的欄位將永遠私有於坦克,即使在重坦克類別上建立一個方法,直接在類別中使用私有化的欄位也是一樣不可行的,這就像秘密永遠就是秘密。在編譯時錯誤訊息告訴我們編號與健康值欄位目前的存取等級是沒有開放存取的。

編譯時會發生錯誤:

小提示小提示不論是欄位、特性與方法,只要是經過私有化,就不會被繼承。

Page 21: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

• 子類別繼承自父類別時發現繼承的方法,執行起來並不能滿足子類別所要求的功能時,這時就可以使用改寫的技巧將方法中的程式內容重寫一次,以滿足子類別所要求的功能。

Page 22: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

如何以重坦克的身份進行顯示狀態?!• 目前坦克( Tank )的顯示狀態方法( Show() ),

在執行的時候會顯示出坦克的編號( ID )、健康值( HP );但現在重坦克( HeavyTank )繼承了坦克,在進行顯示狀態的同時,除了將坦克的一些基本資訊顯示出來外,還需要加上重坦克特有的資訊,例如是否安裝機槍( HasMachineGun )的資訊,那這時怎麼辦呢?顯示狀態方法又是從坦克繼承下來的?!若想要變更顯示狀態方法中的程式內容, 這該如何辦到?這是有可能的嗎?

Page 23: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_1 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();h.ID = "H001";h.HasMachineGun = true;h.Show();}}

建立重坦克型別( HeavyTank )的物件。

同時,也設定了這台重坦克上的是否安裝機槍特性( HasMachineGun )的值為true 。但因為重坦克的顯示狀態方法( Show() )是繼承

自坦克類別( Tank )的,因此並不會將是否安裝機槍特性的資訊顯示出來。執行結果:

編號: H001 健康值: 1000

Page 24: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

改寫方法,讓重坦克以重坦克的身份進行顯示狀態。

• 因為坦克( Tank )的顯示狀態( Show() )方法不再適用於重坦克( HeavyTank )身上,因此我們必須將繼承自坦克的顯示狀態方法重新寫過一次,並將方法中的內容重新以新的程式覆蓋過去,而這個動作就稱為改寫 -Overriding 。為了要進行方法的改寫,必須有兩項工作是必須作的,第 1項就是在父類別-坦克類別上使用 virtual 關鍵字將顯示狀態方法加註為虛擬的;第 2項就是子類別-重坦克類別上使用關鍵字 override 將顯示狀態改寫掉,這樣就完成整個改寫的工作了。

Page 25: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_2 Tank.cs

class Tank{//略過一些程式public virtual void Show(){Console.WriteLine(" 編號: "+ id);Console.WriteLine (" 健康值: "+ hp);}}

將坦克類別( Tank )上的顯示狀態方法( Show() ),在 public與 void回傳型別之間加上一個關鍵字 -virtual, virtual 的意思就是「虛擬的」意思,即有可以被改變的意思。

Tank

ID

HP

Show()

Fire()

BeHit()

HeavyTank

HasMachineGun

Rake()

Show()UML習慣將改寫的方法再一次的寫出來,表示這個方法進行了改寫的動作。就像顯示狀態方法( Show() )在子類別 - 重坦克類別中再出現一次,即表示改寫的意思。

Page 26: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_2 HeavyTank.cs

class HeavyTank : Tank{//略過一些程式public override void Show(){Console.WriteLine(" 這是一台重坦克 ");Console.WriteLine(" 編號: "+ ID);Console.WriteLine(" 健康值: "+ HP);Console.WriteLine(" 是否安裝機槍: "+HasMachineGun);}}

在 public與 void 關鍵字之間加上一個關鍵字- override, override 的意思就是「改寫的」意思,在此將進行顯示狀態方法的改寫。特別記得,只有繼承的方法才有改寫的機會。

改寫主要改變的是方法內容的部份。現在將原本重坦克資訊缺少的部份補上,好讓重坦克可以重坦克的身份進行顯示狀態的行為。

Page 27: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

測試重坦克的顯示狀態方法 ...• 顯示狀態方法( Show() )在重坦克類別( HeavyTank )中已經重新定義過了,除了列出坦克的資訊外,還會列出專屬於重坦克的資訊。現在就讓我們來測試一下:

Page 28: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_2 Tank.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();h.ID = "H001";h.HasMachineGun = true;h.Show();}}

建立重坦克物件( HeavyTank )。

現在是以一個重坦克的身份進行顯示狀態的行為( Show() )。很好 ~~多了重坦克特有的資訊-是否安裝機槍( HasMachineGun )。這就是重坦克改寫顯示狀態方法後,即可以使用重坦克版本的顯示狀態方法,進行坦克狀態的顯示。

執行結果:這是一台重坦克編號: H001 健康值: 1000 是否安裝機槍: true

Page 29: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

改寫重坦克的方法,並不會改變坦克原有的方法。• 改寫是發生在繼承的情況下,而且改寫的對象必須是方法,此方法必須是父類別即有的方法。如此的情況下,才能在子類別再一次的重新定義此方法,以進行方法的改寫。就像是重坦克( HeavyTank )繼承自坦克( Tank )一樣,重坦克將繼承下來的顯示狀態方法( Show() )重新進行改寫,但改寫的同時,並不會改變坦克原本的顯示狀態方法中的程式內容。

Page 30: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_3 Tank.cs

class Program{static void Main(string[] args){Tank t = new Tank();t.ID = "T001";t.Show();HeavyTank h = new HeavyTank();h.ID = "H002";h.HasMachineGun=true;h.Show();}}

坦克( Tank )並不會因為重坦克對顯示狀態( Show() )的改寫而有所改變,它還是會以坦克原本的顯示狀態方法進行坦克狀態的顯示。

重坦克( HeavyTank )會以重坦克的身份進行顯示狀態,即呼叫改寫後的顯示狀態方法。

執行結果:編號: T001健康值: 1000 這是一台重坦克編號: H002健康值: 1000 是否有機槍: true

小提示小提示當然,特性也是可以改寫的,但記住,請先為特性加上 virtual,如此子類別才可以透過 override關鍵字進行改寫。

Page 31: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

重坦克可以透過 base再次的使用坦克上的顯示狀態方法。

• 我們可以使用 base 很明確的告知編譯器,說明要使用的方法是位於父類別上的方法。現在就讓我們看看如何透過 base 使用父類上的方法。如此就可以在重坦克( HeavyTank )改寫顯示狀態方法( Show() )時,再一次的使用坦克( Tank() )的顯示狀態方法了。

Page 32: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_4 HeavyTank.cs

class HeavyTank : Tank{//略去一些欄位、特性與方法public override void show(){Console.WriteLine(" 這是重坦克 ");base.Show();Console.WriteLine(" 是否安裝機槍: "+hasMachineGun);}}

base 有基底類別的意思,可作為父類別-坦克類別的代名詞,因為重坦克( HeavyTank )與坦克透過了繼承產生了父子類別的關係。

透過 base 使用父類別-坦克類別( Tank )的顯示狀態方法,其會顯示編號( id )與健康值( hp )欄位的資訊。

如此重坦克在改寫顯示狀態方法時,只要另外加上重坦克部份的內容就可以了,即顯示是否安裝機槍欄位( hasMachineGun )的資訊。

Page 33: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding

當然,重坦克也可以透過 base使用坦克上的所有特性。

• base既是父類別的代名詞,只要是定義在父類別的特性,也是可以被存取的。就像定義在坦克類別( Tank )上的編號( ID )與健康值( HP )特性一樣,一樣可以透過 base 進行存取。

Page 34: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.2 改寫-Overriding• C7_2_5 HeavyTank.cs

class HeavyTank : Tank{//略去一些欄位、特性與方法public override void show(){Console.WriteLine(" 這是重坦克 ");Console.WriteLine(" 編號: "+base.ID);Console.WriteLine(" 健康值: "+base.HP);Console.WriteLine(" 是否安裝機槍: "+hasMachineGun);}} 雖然可以直接存取繼承坦克類別

而來的編號( ID )與健康值( HP )特性,但您還是可以多此一舉的使用 base 來存取編號與健康值特性。

小提示小提示其實, base 也可以用來存取父類別-坦克類別上的欄位,只要坦克上的欄位不是私有化的,即可進行存取,換句話說,只要將坦克類別上的編號( id )與健康值( hp )欄位改成公開化(使用 public修飾),子類別-重坦克即可透過 base 進行存取。

Page 35: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected

• protected 關鍵字可以讓欄位、特性或方法進行保護化,亦可透過繼承的方式進行存取;相對的,非繼承的類別將不可存取這些保護化後的欄位、特性與方法。

繼承自坦克的重坦克,如何取得真實的健康值?• 現在重新為坦克類別加入新的真實健康值欄位( realHP ),並透過被擊中方法( BeHit() )真實的計算健康值的數值,即便是負數也是一樣。

Page 36: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_1 Tank.cs

class Tank{//略過一些程式private int hp = 1000;private int realHP = 1000;public void BeHit(Bomb b){HP = HP - b.Firepower;realHP = realHP - b.Firepower;}}

健康值欄位( hp )還是一樣是私有化的。現在多一個真實健康值欄位( realHP )來保存真實的健康值的數值。

坦克被擊中( BeHit() )時,會真實的計算真實健康值欄位( realHP )的數值,即便是負數也是一樣。

Page 37: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_1 HeavyTank.cs

class HeavyTank : Tank{//略過一些程式public override void Show()  {Console.WriteLine(" 這是一台重坦克 ");  Console.WriteLine(" 編號: "+ ID);Console.WriteLine (" 健康值: "+HP);Console.WriteLine (" 是否安裝機槍: "+HasMachineGun);}}

因為健康值特性( HP )上的取值存取子的關係,即使在射擊( Fire() )的時候能夠真實的將健康值反應在真實健康值欄位上( readHP ),但重坦克( HeavyTank )還是只能夠透過健康值特性( HP )取得最小值為 0 的健康值。

Page 38: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_1 Program.cs

class Program{static void Main(string[] args){Tank h1 = new Tank();h1.ID = "H001";HeavyTank h2 = new HeavyTank();h2.ID = "H002";h2.HasMachineGun=true;h1.Fire(h2);h1.Fire(h2);h1.Fire(h2);h1.Fire(h2);Console.WriteLine(h2.HP);h2.Show();}}

建立一個重坦克。

即便被射擊了 4 次,真實健康值欄位( realHP )中的值是 -200 。

但因為重坦克只能透過繼承的健康值特性( HP )取得健康值,因此取得的最小數值為 0 。這時要怎麼作才可以讓重坦克也可以取得真實的健康值呢?

執行結果:射擊 ~~~ 射擊 ~~~ 射擊 ~~~ 射擊 ~~~ 0 這是一台重坦克 編號: H002 健康值: 0 是否安裝機槍: True

Page 39: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected

protected讓只有透過繼承的重坦克,存取真實健康值欄位。

• 為了讓重坦克可以取得真實健康值欄位( realHP )中的數值,這時可以使用 protected 關鍵字,讓真實健康值欄位成為保護化的,保護此欄位除了坦克( Tank )本身可存取外,另外只可以透過繼承的方式,被子類別進行存取。

Page 40: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_2 Tank.cs

class Tank{//略過一些程式protected string realHP;}

使用 protected 關鍵字,將真實健康值欄位( realHP )變成保護化的。

Page 41: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_2 HeavyTank.cs

class HeavyTank : Tank{//略過一些程式public void RealShow()  {Console.WriteLine(" 這是一台重坦克 ");  Console.WriteLine(" 編號: "+ ID);Console.WriteLine (" 健康值: "+ realHP);Console.WriteLine (" 是否安裝機槍: "+  HasMachineGun);  }}

新的真實顯示狀態方法( RealShow() )。

因為真實健康值欄位( realHP )是保護化的,因此繼承的重坦克( HeavyTank )類別可以直接存取。記住,類別是為物件所作準備的,因此保護化的欄位也可以說成是物件內部使用的欄位。

Page 42: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_2 Program.cs

class Program{static void Main(string[] args){Tank h1 = new Tank();h1.ID = "H001";HeavyTank h2 = new HeavyTank();h2.ID = "H002";h2.HasMachineGun=true;h1.Fire(h2);h1.Fire(h2);h1.Fire(h2);h1.Fire(h2);h2.RealShow();}}

建立一個重坦克( HeavyTank )。

被射擊了 4 次,真實健康值欄位( realHP )中的值是 -200 。

真實顯示狀態方法( RealShow() )會直接存取保護化的真實健康值欄位( realHP ),因此可以取得真實健康值欄位中真實的數值。

執行結果:射擊 ~~~ 射擊 ~~~ 射擊 ~~~ 射擊 ~~~ 這是一台重坦克 編號: H002 健康值: -200 是否安裝機槍: True

Page 43: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected

當然 protected也可以讓坦克的特性與方法,只被繼承的重坦克存取。

• 其實 protected 也可以讓特性與方法成為保護化的,當然,這些保護化後的特性與方法,也只能透過繼承的方式進行存取。

Page 44: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_3 Tank.cs

class Tank{//略過一些程式protected string hp;protected int RealHP{ get{return hp;} }protected int GetRealHP(){return hp;}}

protected 關鍵字也可以使用在特性與方法上。

Page 45: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_3 Tank.cs

class HeavyTank : Tank{//略過一些程式public void ShowRealHP1(){Console.WriteLine(RealHP);}public void ShowRealHP2(){Console.WriteLine(GetRealHP());}}

保護化的特性與方法,也可以被繼承的重坦克類別( HeavyTank )存取。

Page 46: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected

注意, protected是不對外開放的,只可在繼承的重坦克類別中使用。

• protected保護化後的欄位、特性與方法只能被繼承的類別所使用。

Page 47: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_4 Tank.cs

class Tank{//略過一些程式protected int realHP = 1000;protected int RealHP{get{return realHP;} }protected int GetRealHP(){return realHP;}}

這些欄位、特性與方法皆是透過 protected 關鍵字進行保護化的。

Page 48: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.3 繼承與保護化- Inheritance & protected• C7_3_4 Program.cs

class Program{static void Main(string[] args){HeavyTank h2 = new HeavyTank();Console.WriteLine(h2.hp);Console.WriteLine(h2.ReadHP);Console.WriteLine(h2.GetRealHP());}}

即便建立的是一個重坦克( HeavyTank )物件,但因為保護化的欄位、特性與方法,只能限於重坦克類別中使用,因此若透過重坦克物件來存取,將會發生編譯錯誤的狀況。

編譯發生錯誤:

Page 49: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

• 多型就是指 "多種型別的意思 " ,一個物件可以同時擁有一個以上的型別,但前提是要先有繼承。擁有多個型別的物件是可以一再的轉型的,只要是此物件擁有的型別就可以進行轉型的操作;而物件在進行型別轉換後,其型別將決定物件的可操作範圍。

Page 50: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

讓坦克的角色更多樣化。• 在戰爭的世界上坦克也可分為好幾種,不同的坦

克在功能上皆有所差異,造成每個種類的坦克都會擁有各自的欄位、特性與方法。但不論是什麼種類的坦克,坦克終究是坦克,一定會有坦克基本的欄位、特性與方法。因此我們可以使用繼承的方式,產生不同種類的坦克類別,以表示不同種類坦克之間的差異。現在,讓我們加入新的自爆坦克( BoomTank )與兩棲坦克( AmphibiousTank )吧,但首先,我們還是先回顧一下重坦克( HeavyTank )在繼承後的變化:

Page 51: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_1 HeavyTank.cs

class HeavyTank :Tank{private bool hasMachineGun;public bool HasMachineGun{get{return hasMachineGun;}set{hasMachineGun = value;}}public void Rake(){if(HasMachineGun == true)Console.WriteLine(" 掃射 ~~~~");elseConsole.WriteLine(" 機槍還沒安裝 ...");}}

重坦克( HeavyTank )繼承了坦克( Tank )。Tank

ID

HP

Show()

Fire()

BeHit()

HeavyTank

HasMachineGun

Rake()

Page 52: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_1 AmphibiousTank.cs

class AmphibiousTank : Tank{public void Swim(){Console.WriteLine("游泳中 ...");Console.WriteLine("兩棲坦克 "+ ID + "正在游泳 ...");Console.WriteLine("游泳完畢 ...");}

兩棲坦克( AmphibiousTank )繼承了坦克。並改良成有游泳能力的坦克。

Tank

ID

HP

Show()

Fire()

BeHit()

HeavyTank

Swim()

Page 53: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_1 BoomTank.cs

class BoomTank : Tank{public void BlowUp(){Console.WriteLine("Boom~~~~");}}

嗯~這是自爆坦克( BoomTank ),改良成有自爆能力的自爆坦克。

Tank

ID

HP

Show()

Fire()

BeHit()

HeavyTank

BlowUp()

Page 54: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

改寫再一次的讓不同種類的坦克擁有不同的顯示狀態。

• 每一台不同種類的坦克,包含(重坦克、自爆坦克與兩棲坦克( HeavyTank、 BoomTank與AmphibiousTank )),在進行顯示狀態( Show() )的時候都會先表明坦克的類型,而後才介紹一些相關的坦克資訊。因此顯示狀態方法在各種不同種類的坦克都會不同的表現方式,所以我們就必須進行顯示狀態方法的改寫,這樣才能表示各個不同類別坦克的顯示狀態。

Page 55: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_2 Tank.cs

class Tank{//略去一些程式public virtual void Show(){Console.WriteLine(" 編號: "+ id);Console.WriteLine (" 健康值: "+ hp);}}

記得要先將坦克類別( Tank )的顯示狀態方法( Show() )標示為virtual ,這樣才能被繼承的類別所改寫。

Page 56: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_2 HeavyTank.cs

class HeavyTank : Tank{//略去一些程式public override void Show(){Console.WriteLine(" 這是一台重坦克 ");base.Show();Console.WriteLine (" 是否安裝機槍: "+HasMachineGun);}}

override 是改寫時所用到的字眼,一定要加上去才能夠進行改寫。

這是針對重坦克( HeavyTank )進行改寫的內容,會覆蓋原本的繼承的顯示狀態方法中的程式。

Page 57: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_2 AmphibiousTank.cs

class AmphibiousTank : Tank{//略去一些程式public override void Show(){Console.WriteLine(" 這是一台兩棲坦克 ");base.Show();}}

提醒您, override 要加上去才可以改寫。

換個方式說明;針對自爆坦克( BoomTank )改寫內容的方式,就是在加在 override 的顯示狀態方法的大括號中( { } ),將執行的程式重寫一遍。

Page 58: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_2 BoomTank.cs

class BoomTank : Tank{//略去一些程式public override void Show(){Console.WriteLine(" 這是一台自爆坦克 ");base.Show();}}

再次強調 override 要加上去才可以改寫。

換個方式說明;針對自爆坦克( BoomTank )改寫內容的方式,就是在加在 override 的顯示狀態方法的大括號中( { } ),將執行的程式重寫一遍。

Page 59: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

繼承讓重坦克、自爆坦克與兩棲坦克皆是「 IS-A 」重坦克。

• 繼承除了可以讓重坦克、自爆坦克與兩棲坦克( HeavyTank、 BoomTank、 AmphibiousTank )可以直接的繼承坦克( Tank )的欄位、特性與方法外,其實還有一個最大的特點那就是多重身份的擁有。換句話說,不論您建立的是重坦克的物件、或是自爆坦克的物件、又或是兩棲坦克的物件,其實它們也都是坦克物件。只要是有繼承,那重坦克物件也是坦克的一種,而自爆坦克與兩棲坦克當然也是坦克的一種。

Page 60: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphismclass Program{static void Main(string[] args){HeavyTank h;h = new HeavyTank();IS-A

Amphibious a;a = new Amphibious();

IS-A

IS-ABoomTank b;b = new BoomTank();}}

Tank

ID

HP

Show()

Fire()

BeHit()

BoomTank

BlowUp()

Show()

HeavyTank

HasMachineGun

BlowUp()

Show()

Amphibious

BlowUp()

Show()

Page 61: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

轉型讓重坦克、自爆坦克與兩棲坦克皆可被當作坦克來看待。

• 重坦克、自爆坦克與兩棲坦克( HeavyTank、 BoomTank與 AmphibiousTank )皆是繼承自坦克( Tank )的,因此都可以將這些不同類型的坦克都當作是坦克來看待,就好像大家都是坦克一樣。當然 C# 的物件導向技術也是可以如此表示的。

Page 62: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_3 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();AmphibiousTank a = new AmphibiousTank();BoomTank b = new BoomTank();h.ID = "H001";a.ID = "A001";b.ID = "B001";Tank t1 = h;Tank t2 = a;Tank t3 = b;Console.WriteLine(t1.ID);Console.WriteLine(t2.ID);Console.WriteLine(t3.ID);}}

分別建立重坦克、兩棲坦克與自爆坦克( HeavyTank、 AmphibiousTank與BoomTank )的物件。

還記得型別的功能嗎?!沒錯,就是要限制變數中所裝的東西。因此若型別是坦克型別( Tank ),則這個變數就只可裝下坦克型別的物件。

不論是重坦克、兩棲坦克或是自爆坦克,都是可以被視為坦克物件並放入坦克型別的變數中保存。而這個動作會進行型別轉型的動作;轉型讓重坦克、兩棲坦克與自爆坦克轉換型別為坦克型別的物件後放入坦克型別的變數中。

被當作坦克對待的同時,原本有的資料是會被保留下來的。它們還是一樣會記得他們的編號。

Page 63: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

注意,轉型成坦克型別後,就要以坦克的身份來對待。

• 當您成功將重坦克、自爆坦克與兩棲坦克物件( HeavyTank、 BoomTank與 AmphibiousTank )放進坦克型別( Tank )的變數中時,這時將會以坦克的身份來看待這些物件;既然以坦克的身份來看待,就表示能使用的欄位、特性與方法都將限定在坦克類別的範疇,這時不論是重坦克、自爆坦克與兩棲坦克物件,它們所能操作的欄位、特性與方法,將只限於坦克類別所規定的範疇內。

Page 64: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_4 Program.cs

class Program{static void Main(string[] args) {HeavyTank h = new HeavyTank();AmphibiousTank a = new AmphibiousTank();BoomTank b = new BoomTank();Tank t1 = h;Tank t2 = a;Tank t3 = b; t1.Rake();t2.Swim();t3.BlowUp();}}

雖然分別建立重坦克、兩棲坦克與自爆坦克( HeavyTank、AmphibiousTank與 BoomTank )的物件。

但已經轉型為坦克型別( Tank )並放入坦克型別的變數中。錯誤!重坦克已轉換型別為坦

克型別了,所以不能進行掃射方法( Rake() )。錯誤!兩棲坦克已轉換型別為坦克型別了,所以不能進行游泳方法( Swim() )。

錯誤!兩棲坦克已轉換型別為坦克型別了,所以不能進行自爆方法( BlowUp () )。當物件轉換為坦克型別時,就必須用坦克的角度來看這個物件;原因就出在坦克類別上並沒有自爆方法可以使用。當物件的型別轉換為坦克型別時,就必須用坦克的角度來看這個物件。

編譯時發生錯誤:

Page 65: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

不論重坦克、自爆坦克與兩棲坦克都不會忘記改寫所許下的承諾,即使轉型之後也是如此。

• 雖然重坦克、自爆坦克與兩棲坦克的物件( HeavyTank、 BoomTank與 AmphibiousTank ),可以轉型為坦克型別( Tank )的物件來看待,但他們在操作一些改寫過的特性或方法時,還是會保有改寫過後的程式功能。就像是顯示狀態( Show() )方法一樣,重坦克、自爆坦克與兩棲坦克的物件還是會執行改寫後的顯示狀態方法,即使已經轉型為坦克型別也是一樣。

Page 66: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_5 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();h.ID="H001";h.HasMachineGun=true;AmphibiousTank a = new AmphibiousTank();a.ID="A001";BoomTank b = new BoomTank();b.ID="B001";Tank t1 = h;Tank t2 = a;Tank t3 = b;t1.Show();t2.Show();t3.Show();}}

分別建立重坦克、兩棲坦克與自爆坦克( HeavyTank、 AmphibiousTank與BoomTank )的物件。

但已經轉型為坦克型別( Tank )並放入坦克型別的變數中。執行顯示狀態方法時還是會以改寫過後的顯示狀態方法來執行,即使已經轉型為坦克型別,但還是改變不了這些坦克分別是重坦克、兩棲坦克與自爆坦克的事實,因此它們皆會以改寫後的顯示狀態方法來執行。

執行結果:這是一台重坦克 編號: H001 健康值: 1000 是否安裝機槍: True這是一台兩棲坦克 編號: A001 健康值: 1000 這是一台自爆坦克 編號: B001 健康值: 1000

Page 67: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

使用強迫轉型表明坦克的真實身份。• 當重坦克、自爆坦克與兩棲坦克的物件( HeavyTank、 BoomTank與 AmphibiousTank )分別轉變型別為坦克型別( Tank )後,其實是可以在改變回原本的型別的。但不同的地方是若要從坦克型別轉型回即有的型別,這時必須使用強迫轉型,明確的告知編譯器所要轉變的型別,這樣才能變回原本即有的型別。

Page 68: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_5 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();h.ID="H001";h.HasMachineGun=true;Tank t = h;HeavyTank h1 = (HeavyTank) t;h1.HasMachineGun=true;h1.Rake();}}

建立一個重坦克物件( HeavyTank )。

先轉型為坦克型別( Tank )的物件,並放入 t變數中。

使用強迫轉型(就是在物件前加上小括號,小括號中放著希望轉變的型別)的方式將物件的型別作轉換。為什麼一定要使用強迫轉型呢?!原因很簡單,就是繼承自坦克的類別有重坦克、自爆坦克( BoomTank )與兩棲坦克( AmphibiousTank )等 3 個類別。因此編譯器不能肯定您的 t變數中的物件原本的型別就是重坦克型別,因此我們要使用強迫轉型,明確的告知編譯器這個物件原本的型別,這樣才能夠完成轉型的動作。

這個坦克物件最後變回了重坦克的身份,並保存在 h1變數中,同時它也恢復了使用是否安裝機槍特性( HasMachineGun )與掃射( Rake() )方法的能力。

Page 69: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism

現在將軍檢查所有坦克的狀態。• 將軍( General )要檢查每台坦克( Tank )的狀

態了,不論您是哪一種類型的坦克,對將軍而言都是「坦克」,只要是坦克皆可以透過顯示狀態方法( Show() )來了解坦克的狀態。還記得我們分別對重坦克、兩棲坦克與自爆坦克( HeavyTank、 BoomTank與 AmphibiousTank )改寫了顯示狀態方法嗎!換句話說,即使我們將不同類型的坦克轉型為坦克來看待,它們都還會記得當初改寫顯示狀態方法所定下的規則,因此將軍在檢查坦克狀態的時候,還是可以得到正確的資訊,例如:重坦克會揭露其是否安裝機槍( HasMachineGun )的資訊。

Page 70: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_7 General.cs

class General{public void CheckTanks(Tank[] allTank){foreach (Tank t in allTank){t.Show();}}}

新的將軍類別( General ),其擁有一個檢查坦克方法( CheckTanks ),會以坦克陣列的方式接收坦克物件。

依序取出陣列中的坦克物件至 t變數當中。而 t變數的型別同樣也是坦克型別( Tank )。雖然 t變數裡放的是坦克型別的物件,

但在顯示狀態( Show() )的時候,還是會依照此物件原型別改寫的顯示狀態方法來進行狀態的顯示。

Page 71: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.4 多型- Polymorphism• C7_4_7 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();AmphibiousTank a = new AmphibiousTank();BoomTank b = new BoomTank();h.ID="T001";h.HasMachineGun = true;a.ID="T002";b.ID="T003";Tank[] ts = {h, a, b};General g = new General();g.CheckTanks(ts);}}

分別建立重坦克、兩棲坦克與自爆坦克( HeavyTank、 AmphibiousTank與BoomTank )的物件。

將重坦克、兩棲坦克與自爆坦克的物件,都轉型為坦克物件後放入坦克陣列變數 -ts 中。建立一個將軍( General )物件。

並透過檢查坦克方法( CheckTanks() ),檢查所有坦克的狀態。即使在檢查時將軍會將所有物件視為坦克型別的物件,但實際上每台坦克皆會依改寫後的顯示狀態方法,進行坦克狀態的顯示。

執行結果:這是一台重坦克編號: T001 健康值: 1000 是否安裝機槍: True 這是一台兩棲坦克 編號: T002 健康值: 1000 這是一台自爆坦克 編號: T003 健康值: 1000

小提示小提示還記得我們有個飛彈( Bomb)類別嗎!這是個伏筆,您可自行練習透過繼承與多型的觀念建置不同的飛彈,並為每個不同類型的坦克在射擊時發射不同的飛彈,並介由多型的觀念,只要是飛彈都可以擊中坦克,因此我們可以在不改變被射擊( BeHit())方法的情況下改變飛彈的類型,以提供不同威力的飛彈的可能。

Page 72: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

• 使用 abstract 關鍵字將類別抽象化,那這個類別即稱為抽象類別。抽象類別的目的就是防止實體物件的建立,換句話說,抽象類別是不可以透過new 關鍵字來建立實體物件的。而 abstract-抽象的意思,即代表著某些東西是以抽象的方式存在著,因此不可建立此抽象類別的實體物件。

Page 73: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

建立的坦克物件,即不是重坦克、兩棲坦克或自爆坦克,那為什麼還要建立坦克物件呢?!

• 重坦克、兩棲坦克或自爆坦克類別( HeavyTank、 BoomTank與 AmphibiousTank )皆是繼承自坦克類別( Tank )的,因此皆可以視為坦克的一種,甚至可以轉型成坦克型別,以視為坦克來看待。但若是您建立的是坦克類別的物件呢?甚至當您想將這個坦克物件強迫轉型為重坦克、兩棲坦克或自爆坦克類別的型別物件,執行時將會產生一個異常,告訴您這個坦克物件根本就不是重坦克、兩棲坦克或自爆坦克中的任何一類的物件。而對於將軍( General )而言,當然是製作有特殊功能的坦克比較划算,那有沒有一個好方法可以避免坦克類別( Tank )的物件被建立呢? C# 是否有提供這類防犯的功能或語法呢?

Page 74: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_1 Program.cs

class Program{static void Main(string[] args){Tank t = new Tank();t.ID = "T001";HeavyTank p = (HeavyTank) t;BoomTank b = (BoomTank) t;AmphibiousTank a = (AmphibiousTank) t;}}

建立的是一個純坦克型別( Tank )的物件。

t變數中的是一個純坦克物件,因此是無法轉型成重坦克型別( HeavyTank )的物件(又或是自爆坦克物件、兩棲坦克物件( BoomTank、 AmphibiousTank )),因為強迫轉型的關係,表示我們很明確的告訴編譯器這就是一個重坦克物件,因此編譯成功。但到實際執行的時候呢?您會得到一個異常物件,告訴您此坦克物件無法轉型為重坦克物件。

執行時發生異常:

Page 75: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

使用 abstract抽象化您的坦克類別。• 要讓類別抽象化必須使用 abstract 關鍵字來對類別

進行修飾。抽象化的類別也是可以作為一個型別來使用,並不會因為抽象化後,而不能作為其他物件的型別;因此建立的重坦克物件、兩棲坦克物件或自爆坦克物件( HeavyTank、 BoomTank與 AmphibiousTank )皆是可以轉型為坦克型別( Tank )的。

Page 76: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_2 Tank.cs

abstract class Tank {//略去一些程式}

abstract 關鍵字加在 class 關鍵字的前面,代表著此類別-坦克類別( Tank )將會被定義為抽象化的。

• C7_5_2 Tank.cs

class Program{static void Main(string[] args){Tank e1 = new HeavyTank();Tank e2 = new BoomTank();Tank e3 = new AmphibiousTank ();}}

抽象化的坦克類別,一如往常的可以作為型別來使用。在這段程式中,重坦克物件、自爆坦克物件或兩棲坦克物件( HeavyTank、 BoomTank與 AmphibiousTank )皆是可以轉型為坦克型別的物件的。

Page 77: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

注意,抽象化後的坦克類別,可以防止坦克物件被new出來。

• 將類別抽象化的最主要目的,就是用來防止物件被建立(透過 new ),就像我們的抽象化的坦克類別( Tank ),就可以防止坦克型別的物件直接以 new 的方式建立。如此就不會再有建立的坦克即不是重坦克、兩棲坦克或自爆坦克( HeavyTank、 BoomTank與 AmphibiousTank )的問題了。因此當我們嘗試著使用 new 來建立坦克類別的實體物件,這時編譯器會發生錯誤。

Page 78: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_2 Tank.cs

class Program{static void Main(string[] args){Tank e1 = new Tank();}}

嘗試著透過 new 來建立坦克類別( Tank )的物件,但因為坦克類別是抽象化的,因此在編譯的時候,編譯器會發生錯誤,告訴您不可建立抽象化的坦克類別的物件。編譯時發生錯誤:

Page 79: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

抽象化坦克的顯示狀態方法,讓您不必先為不同類型的坦克的顯示狀態煩心。

• 方法也可以透過 abstract 關鍵字進行抽象化。當方法變成抽象化時,即可以不必給予方法本體(也就是方法後面的大括號),如此就不需要事先定義此方法要如何進行運作了。就像坦克的顯示狀態方法( Show() ),所有繼承自坦克類別( Tank )的子類別-重坦克類別、兩棲坦克類別與自爆坦克類別( HeavyTank、 BoomTank與AmphibiousTank )都是會改寫顯示狀態方法的,因此在坦克類別定義顯示狀態方法時即可以使用abstract 關鍵字將方法抽象化,直到子類別透過改寫的方式將顯示狀態方法的本體加上。

Page 80: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_4 Tank.cs

abstract class Tank {private string id;private int hp = 1000;public abstract void Show() ;}

abstract 關鍵字也可以將方法變為抽象化的。與抽象類別的目的不同之處在於描象方法是可以不必加上方法本體(即方法後面的大括號 {} ),即表示可以不必事先決定方法要如何執行;如此就不必先為坦克的顯示狀態方法煩心。

雖然說不用加上方法本體(即大括號),但必須額外加上一個分號( ; )以表示此抽象方法到此結束。

在此範例將方法設為 public ,讓繼承的類別可以看到此方法,並進行實作。

Page 81: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

各類型的坦克類別必須實作抽象化的顯示狀態方法,以決定顯示狀態時的顯示資訊。

• 重坦克類別、兩棲坦克類別與自爆坦克類別( HeavyTank、 BoomTank與 AmphibiousTank )皆是繼承自抽象化的坦克類別( Tank ),因此需要實作(即改寫,但針對抽象方法的改寫會稱之為實作,即實作新方法之意)繼承而來的抽象方法 - 顯示狀態方法( Show() ),並加上方法本體,以描述方法要如何執行,如此才能完成顯示狀態方法的定義。

Page 82: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_5 HeavyTank.cs • C7_5_5 BoomTank.cs

class HeavyTank : Tank{//略過一些程式public override void Show(){Console.WriteLine(" 這是一台重坦克 ");Console.WriteLine(" 編號: " + ID);Console.WriteLine(" 健康值: " + HP);Console.WriteLine(" 是否安裝機槍: " + HasMachineGun);}}

class BoomTank : Tank {//略過一些程式public override void Show(){Console.WriteLine(" 這是一台自爆坦克 ");Console.WriteLine(" 編號: " + ID);Console.WriteLine(" 健康值: " + HP);}}

只要是繼承自坦克類別( Tank )的子類別,都必須將繼承的抽象方法-顯示狀態方法( Show() )實作(在此因實作的對象是抽象化的,因此又可稱為實作),並加上方法本體(即大括號),如此才能完成顯示狀態方法的定義。

Page 83: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_5 AmphibiousTank.cs

class AmphibiousTank : Tank {//略過一些程式public override void Show() {Console.WriteLine(" 這是一台兩棲坦克 ");Console.WriteLine(" 編號: " + ID);Console.WriteLine(" 健康值: " + HP);}}

當然,自爆坦克也要進行顯示狀態方法( Show() )的改寫,如此才能完成顯示狀態方法的定義。

Page 84: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_5 BoomTank.cs

class Program{static void Main(string[] args)HeavyTank h = new HeavyTank();BoomTank b = new BoomTank();AmphibiousTank a = new AmphibiousTank();h.ID="H001";h.HasMachineGun=true;b.ID ="H002";a.ID ="H003";h.Show();b.Show();a.Show();}}

分別建立重坦克、兩棲坦克與自爆坦克( HeavyTank、 AmphibiousTank與 BoomTank )的物件。這些不同類型的坦克物件,皆

有各自改寫的顯示狀態方法。

Page 85: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

注意,非抽象化的各類坦克類別必須改寫抽象的顯示狀態方法。

• 非抽象類別是必須改寫(實作)繼承的所有抽象方法,否則會發生編譯失敗。問題很單純,就是非抽象類別是可以建立實體物件的,若繼承的抽象方法沒有實作的話,則建立出來的物件將不知如何執行這個抽象方法,因此必須實作抽象方法,讓抽象方法有方法本體(即大括號)。

Page 86: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_6 HeavyTank.cs

• C7_5_6 BoomTank.cs

• C7_5_6 AmphibiousTank.cs

class HeavyTank : Tank{//略過一些程式public override void Show(){ }}

class BoomTank : Tank {//略過一些程式public override void Show(){ }}

class AmphibiousTank : Tank {//略過一些程式public override void Show(){ }}

若非抽象化的子類別,繼承自抽象類別時,必須實作所有的抽象方法,若不實作則會發生編譯錯誤,告訴您這是一個具象的類別(就是非抽象的意思),您必須實作所有的抽象方法。這個原理很單純,只要是非描象化的類別,皆可以透過 new 來建立物件,但若是這個物件的類別繼承了未實作的抽象方法呢(就是沒有方法本體)?那不就不知道如何執行這個方法了嗎?所以非抽象的類別一定要實作所有繼承的抽象方法。

編譯時會發生錯誤:

Page 87: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

抽象化的 X 坦克類別,可以不必改寫抽象的顯示狀態方法。

• 若想要讓子類別不必實作繼承的抽象方法,這時就要將這個子類別也使用 abstract 關鍵字將子類別也進行抽象化的動作,如此就不必實作繼承的抽象方法了(但若是有需要還是可以實作繼承的抽象方法的)。原因很單純,若子類別也是抽象類別時,則不會有透過 new 建立實體物件的可能,因此即使沒有實作繼承的抽象方法,也不會被拿來誤用這個沒有方法本體的抽象方法。現在我們另建立一個 X 坦克類別( XTank ),它是抽象化且繼承自坦克類別( Tank )的,但因為 X 坦克是抽象化的,因此可以不必改寫抽象化的顯示狀態方法( Show() )。

Page 88: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_7 XTank.cs

• C7_5_7 XTank.cs

abstract class XTank : Tank{public override void Show(){ }}

class Program{static void Main(string[] args)XTank x = new XTank();}}

當子類別 -X 坦克( XTank )也被定義為抽象類別時,則這個子類別可以不必實作繼承下來的抽象方法。

因為抽象方法是不可以透過 new 建立實體物件的,因此也不用擔心未有方法內容的抽象方法被拿來使用,進而造成抽象方法不知如何執行的情況。

Page 89: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class

注意,只有同為抽象化的坦克類別,可以擁有抽象化的顯示狀態方法。

• 當一個類別中有抽象方法時,則這個類別也必須是一個抽象類別。原因就是抽象方法是沒有方法本體的(即大括號),為了要防止這個沒有方法本體的抽象方法被誤用,則類別也必須是抽象化的,而抽象化的類別是不可以建立實體物件的,因此也不擔心沒有方法本體的抽象方法被誤用。就像抽象化的顯示狀態方法( Show() ),必須存在於抽象類別上,因此坦克類別( Tank )也必須是抽象化的。

Page 90: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.5 抽象類別- abstract class• C7_5_8 Tank.cs

abstract class Tank { private string id; private int hp = 1000; //略過一些程式 public abstract void Show() ; }

當一個類別中有抽象方法時,則這個類別也必須是一個抽象類別。若不將類別也定義為抽象的,這時編譯器會發生錯誤,告訴您抽象方法是必須存在於抽象類別中的。

編譯時會發生錯誤:

Page 91: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

• 介面是一種能力( function )的描述,主要用來描述某種能力下應該擁有哪些行為;當然, C# 即是使用方法來描述行為的。在介面上所有的方法皆是公開化( public )且抽象化的( abstract ),因此實作此介面的非抽象化類別,必須實作介面的所有方法,以描述在此介面(能力)下,這些方法要如何進行?介面與類別不同的地方是,一個類別可以繼承自多個介面,但一個類別只能繼承自一個類別。

Page 92: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

有個多功能的坦克多好,這樣就可以不用建立不同類型的坦克了,但這可能嗎?!

• 將軍會希望坦克( Tank )是多功能的,希望有個全能的超級坦克( SuperTank ),擁有重坦克、兩棲坦克與自爆坦克( HeavyTank、 BoomTank與 AmphibiousTank )的所有能力。但這可能嗎?直至目前為止重坦克、兩棲坦克與自爆坦克類別皆是獨立的,它們皆有各自能力之下的行為(即方法),例如重坦克有掃射的行為( Rank() 方法),兩棲坦克擁有游泳的行為( Swim() 方法),自爆坦克有自爆的行為( BoomUp() 方法);若要同時擁有這些坦克的行為,則必須透過繼承的方式來繼承這些方法。但若您建立一個類別,並同時繼承重坦克、兩棲坦克與自爆坦克類別,則編譯器會出錯,告訴您一次只能繼承一個類別。有沒有一個好方法,可以讓這個超級坦克( SuperTank )可以同時擁有多項能力呢?

Page 93: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_1 Tank.cs

class SuperTank :HeavyTank, BoomTank, AmphibiousTank{}

我們將這個多功能的超級坦克類別,取名叫SuperTank 。在3個不同類型坦克的能力都會的情況下,那這真的是很 SuperTank…

您希望透過逗點( , )的方式來表示一次要繼承自多個類別,以取得每個類別的所有方法。但在編譯的時候,編譯器會告訴您這個語法是不對的,因為 1 個類別只能以單一繼承的方式,繼承另一個類別。

編譯時會發生錯誤:

Page 94: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

interface讓重坦克、兩棲坦克與自爆坦克的能力介面化了。

• 為了要定義超級坦克類別( SuperTank ),且同時

擁有重坦克、兩棲坦克與自爆坦( HeavyTank、 BoomTank與 AmphibiousTank )的所有能力。首先,要先將重坦克、兩棲坦克與自爆坦克的能力分離出來,並透過介面來定義每個能力下有哪些行為(即方法)與特性是在此能力下擁有的。

Page 95: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_2 Tank.cs

interface IRake{public abstract bool HasMachineGun {get;set;}public abstract void Rake();}interface IBlowUp{public abstract void BlowUp();}interface ISwim{public abstract void Swim();}

interface 關鍵字表示介面的意思,其是用來定義介面的。

將重坦克( HeavyTank )的能力以介面的方式定義,而這項能力就稱為「掃射」 -IRake ;在 .Net 中所有的介面名稱皆會多加上個「 I」,表示介面的意思。

這是在掃射介面下所擁有的特性與方法,用以表示在掃射的能力下,其擁有是否安裝機槍特性( HasMachineGun )與掃射方法( Rake() )。在介面上的方法是不需要有本體的(即大括號),同時也不能有方法本體的,原因是這些方法皆是抽象的,因此不能有方法本體。

將自爆坦克( BoomTank )的能力以介面的方式定義,而這項能力就稱為「自爆」 -IBlowUp ,在這個能力下有個自爆的行為,因此有一個自爆方法( BlowUp() )。

將兩棲坦克( Amphibious )的能力以介面的方式定義,而這項能力就稱為「游泳」 -ISwim 。在這個能力下有個游泳的行為,因此有一個游泳方法( Swim() )。

public與 abstract兩個修飾關鍵字,是一定會加在方法上的。換句話說,即使不寫 public與abstract ,編譯器會幫您加上,但特別的是,你不可以自已加上。因此所有在介面上的方法,一定是公開化且抽象化的。

Page 96: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

各類型的坦克可以實作不同能力的介面,以擁有特定的能力。

• 類別要實作一個介面,是在需要實作介面的類別後面加上冒號( : )並加上介面的方式完成的,但若是已經有使用過冒號(有可能已經繼承自某個類別),則這時只要再使用逗號( , )在加上介面即可實作此介面;當然,介面上所有的特性與方法都必須實作,因為介面上的特性或方法皆是抽象的。另外介面上的特性或方法皆是使用public 進行公開化,因此在改寫的時候也必須加上 public 關鍵字,否則會編譯失敗。

Page 97: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_2 HeavyTank.cs

class HeavyTank : Tank , Rake{//略過一些程式public void Rake() {if (hasMachineGun == true)Console.WriteLine(" 掃射 ~~~~");elseConsole.WriteLine(" 機槍還沒安裝 ...");}private bool hasMachineGun;public bool HasMachineGun{get { return hasMachineGun; }set { hasMachineGun = value; }}}

若已經有使用冒號繼承類別或實作其他介面時,這時可使用逗號( , ),即可以讓重坦克類別( HeavyTank )實作掃射介面( IRake ),同時必須實作掃射介面上的是否安裝機槍特性( HasMachineGun )與掃射方法( Rake() ),如此重坦克類別就擁有掃射的能力了。特別注意的是,實作介面的特性與方法時,並不需要使用 override 關鍵字進行改寫。

Page 98: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_2 BoomTank.cs

• C7_6_2 AmphibiousTank.cs

class BoomTank : Tank , IBlowUp {//略過一些程式public void BlowUp(){Console.WriteLine( "Boom~~~~");}}

class AmphibiousTank: Tank , ISwim{//略過一些程式public void Swim (){Console.WriteLine("游泳中 ...");Console.WriteLine("兩棲坦克 " + ID + "正在游泳 ...");Console.WriteLine("游泳完畢 ...");}}

使用逗號( , ),即可以讓自爆坦克類別( BoomTank )實作自爆介面( IBlowUp ),同時必須實作自爆介面上的自爆方法( BlowUp() ),如此自爆坦克類別就擁有自爆的能力了。

使用逗號( , ),即可以讓兩棲坦克類別( AmphibiousTank )實作游泳介面( ISwim ),同時必須改寫游泳介面上的游泳方法( Swim() ),如此兩棲坦克類別就擁有游泳的能力了。

Page 99: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

超級坦克可同時實作掃射、自爆、游泳等 3 個介面,以打造超級坦克。

• 現在建立超級坦克類別( SuperTank )的理想要完成了,我們透過介面可以多重實作的特性,讓超級坦克類別可以同時擁有掃射介面、自爆介面與游泳介面( IRank、 IBlowUp與 ISwim) 3 種不同能力的介面。如此只要在實作每個介面的特性與方法,即可讓超級坦克類別一次擁有多種能力。

Page 100: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_3 SuperTank.cs

class SuperTank : Tank , IRake , IBlowUp , ISwim {//略去一些程式public void Rake() {if (hasMachineGun == true)Console.WriteLine(" 掃射 ~~~~");elseConsole.WriteLine(" 機槍還沒安裝 ...");}private bool hasMachineGun;public bool HasMachineGun{get { return hasMachineGun; }set { hasMachineGun = value; }}public void BlowUp(){Console.WriteLine( "Boom~~~~");}public void Swim (){Console.WriteLine("游泳中 ...");Console.WriteLine("兩棲坦克 " + ID + "正在游泳 ...");Console.WriteLine("游泳完畢 ...");}}

超級坦克類別( SuperTank )可以一次實作多個介面,代表著可以同時擁有多種不同的能力。實作多個介面時,只要使用逗號( , )的方式即不同的介面隔開即可。

實作每個介面上的特性與方法,以實際的透過特性或方法本體描述各方法進行的方式。

Page 101: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

透過介面可相互進行多重繼承的特性,累加出全功能介面。

• 介面與介面之間是可以相互繼承的,且是可以多重繼承的。因此我們可以定義全能力介面( IFullFucntion ),在讓此介面繼承掃射介面、自爆介面與游泳介面( IRank、 IBlowUp與ISwim ),如此這個全能力介面就會擁有 3 種介面的能力。

Page 102: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_4 interface.cs

interface IFullFucntion :IRake , IBlowUp , ISwim {}

新的全能力介面- IFullFunction 。介面與介面在進行繼承時,也是透過冒號( : )來達成。

全能力介面( IFullFucntion )可以一次繼承自多個介面,只要使用逗號( , )的方式將不同的介面隔開即可,如此這個全能力介面即可以將其他介面的方法繼承下來。

IRakeIRakeHasMachineGun

Rake()

IFullFunctionIFullFunction

ISwimISwim

Swim()

IBlowUpIBlowUp

BlowUp()

介面是可以多重繼承自一個以上的介面的。

Page 103: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_4 SuperTank.cs

class SuperTank : Tank , FullFucntion {//略過一些程式public void Rake() {if (hasMachineGun == true)Console.WriteLine(" 掃射 ~~~~");elseConsole.WriteLine(" 機槍還沒安裝 ...");}private bool hasMachineGun;public bool HasMachineGun{get { return hasMachineGun; }set { hasMachineGun = value; }}public void BlowUp(){Console.WriteLine( "Boom~~~~");}public void Swim (){Console.WriteLine("游泳中 ...");Console.WriteLine("兩棲坦克 " + ID + "正在游泳 ...");Console.WriteLine("游泳完畢 ...");}}

因為實作全能力介面( IFullFucntion )後,即必須實作掃射介面、自爆介面與游泳介面( IRake、 IBlowUp與ISwim )上的所有特性與方法,原因就出在全能力介面繼承了掃射介面、自爆介面與游泳介面。

Page 104: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

將軍也可以依照不同能力的介面,來進行坦克的篩選。• 介面也是型別的一種,因此我們可以透過介面型

別的檢測,來依照能力進行坦克( Tank )的篩選。

Page 105: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_5 Program.cs

class Program{static void Main(string[] args){HeavyTank h = new HeavyTank();BoomTank b = new BoomTank();AmphibiousTank a = new AmphibiousTank();SuperTank s = new SuperTank();h.ID="T001"; b.ID="T002"; a.ID="T003"; s.ID="T004";Tank[] allTanks = {h, b, a, s};foreach(Tank t in allTanks){if(t is IRake)Console.WriteLine(t.name+" 擁有掃射的能力 ");if(t is IBlowUp)Console.WriteLine (t.name+" 擁有自爆的能力 ");if(t is ISwim)Console.WriteLine (t.name+" 擁有游泳的能力 ");if(t is IFullFucntion)Console.WriteLine (t.name+" 擁有全能力的能力 ");} }}

建立重坦克、自爆坦克、兩棲坦克與超級坦克( HeavyTank、 BoomTank、 AmphibiousTank與 SuperTank )的物件。將重坦克、自爆坦克、兩棲

坦克與超級坦克的物件放入至坦克型別( Tank )的陣列中,這時所有的物件皆將會轉型為坦克型別的物件。

但我們的物件是可以一次擁有多種型別,因此轉型對物件而言,只是型別的暫時改變,而實際擁有的型別是不會變的。當然介面也是型別的一種,因此可以透過 is 關鍵字來測試物件所擁有的型別,若是此物件擁有此型別,則會回傳true ,否則會回傳false 。相對的,當將軍要找出特定能力的坦克時,就可以依照介面來篩選坦克,而不是依照類別來篩選坦克。

Page 106: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface

注意,掃射介面、自爆介面、游泳介面與全能力介面,是不可以 new出物件的。

• 介面是不可以建立實體物件的,原因是介面一定是抽象化( abstract );介面並不像類別一樣,類別是用來描述物件所擁有的資訊(即欄位或特性)與行為(即方法),之後在透過 new 來建立此類別格式的實體物件。介面的功能只是用來描述能力下應該有哪些特性與行為(即方法),在介面中的方法是不會有方法本體的(即大括號),所以在意義上介面是完全與類別不相同的,因此無法透過 new 來建立介面的實體物件。

Page 107: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.6 介面- interface• C7_6_6 Program.cs

abstract interface IRake {bool HasMachineGun { get; set; }void Rake();}class Program{static void Main(string[] args){IRake r = new IRake ();}}

abstract 關鍵字是編譯器為所有的介面加上的修飼字。即使不寫編譯器也會偷偷的為介面加上。

因為介面是抽象化的( abstract ),因此當您嘗試透過 new 建立介面物件時,編譯器會發生錯誤,告訴您介面是抽象化的,您不可建立抽象化型別的物件。

編譯時發生錯誤:

Page 108: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.7 Object類別-Object

• 在 C# 的物件導向中,所有的類別皆會繼承自Object 類別,這是所有類別的父類別。只要類別是沒有繼承自其他類別的類別,編譯器皆會自動為此類別繼承自 Object 類別;當然,您也可以手動的繼承自 Object 類別。

Page 109: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.7 Object類別-Object

坦克類別隱藏的父類別-Object類別。• Object 類別是所有類別的父類別,編譯器會自動

為所有自訂的類別繼承自 Object 類別。就像是編譯器會為坦克類別( Tank )自動繼承自 Obejct 類別一樣;當然,您也可以手動為坦克類別繼承自Object 類別。

Page 110: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.7 Object類別-Object• C7_7_1 Tank.cs

class Tank : Object{//略過一些程式}

ObjectObject

toString()

BoomTankBoomTank

BlowUp()

Show()

TankTankID

HP

Show()

Fire()

BeHit()

IBlowUpIBlowUpHasMachineGun

Rake()

Show()

AmphibiousAmphibiousTankTank

Swim()

Show()

因為坦克類別( Tank )繼承自 Object 類別,因此重坦克( HeavyTank )、自爆坦克( BoomTank )與兩棲坦克( AmphibiousTank )也會間接繼承自Object ,因此這些坦克型別,皆是可以轉型為Obejct 的型別。

編譯器會自動為坦克類別繼承自 Object類別。

Page 111: 物件的多重身份- 繼承與多型

掌握 C# 4.0 設計概要

7.7 Object類別-Object• C7_7_1 Bomb.cs

class Bomb : Object{//略過一些程式}

當然您也可以手動為類別繼承自 Object 類別。

小提示小提示當然飛彈類別( Bomb )也是會自動繼承自 Object 類別的。 Object 類別上有一些常用的方法,例如 toString() 方法,就是將物件轉換為字串( string )型別的方法,也是 Console.Write()與 Console.WriteLine() 方法會自動呼叫 toString() 方法,將物件轉為字串後印出的主要方法。