--- tags: abstract class, interface, C# --- <!-- 使用黑色主題 --> {%hackmd BkVfcTxlQ %} <!-- 決定 CSS 樣板 --> {%hackmd @aidan/inc_hackmd_css %} <span class="TopTitle">C# 抽象型別(abstract class, interface)</span> === * <font color="#f6f">用 abstract 宣告的型別,不能接在 new 後面。</font> * <font color="#f6f">用 interface 宣告的型別,不能接在 new 後面。</font> ## <span class="Title">主題1. 再來看看 Animal 類別</span> ### <span class="SubTitle">Shout</span> 下圖紅色框框處的程式碼並沒有實際的作用 ![](https://i.imgur.com/5FzBW22.png) ### <span class="SubTitle">Lab1-1 **<font color="#06f">abstract</font> 的出現,就不需要上圖紅色框框的程式碼了**</span> **Animal Class** ```C#= abstract class Animal { /// <summary> /// Field (欄位) /// </summary> public int shout_num; public String name; protected string kind; /// <summary> /// Property (屬性) /// </summary> public string Kind { get { return kind; } } /// <summary> /// Constructor (建構函式) /// </summary> public Animal() { this.name = "No-Name"; } public Animal(String name, int shout_num) { this.name = name; this.shout_num = shout_num; } /// <summary> /// Method (方法) /// </summary> /// <returns></returns> public abstract String Shout(); } ``` :::spoiler 補充說明 * public <font color="#06f">abstract</font> String Shout(); 是一個<font color=Green>抽象方法</font>,只能宣告不能實作。 白話文:不能實作的意思就是不能加上 {},然後在{}裡面撰寫程式碼 * 類別成員只要有一個 <font color="#06f"> abstract </font>,那這個類別本身就得加上 <font color="#06f">abstract</font> 舉個例:這段程式碼出現兩次 <font color="#06f">abstract</font>,因為 Shout()本身是一個<font color=Green>抽象方法</font>,所以 Animal 類別本身也要加上 <font color="#06f">abstract</font>。 * 除了 virtual,<font color="#06f">override </font>也可以跟<font color="#06f"> abstract </font>搭配 * 繼承自抽象類別的子類別,一定得實作<font color=Green>抽象方法</font>的程式碼,但是要把 <font color="#06f">abstract</font> 替換為 <font color="#06f">override</font>(如下圖) 再舉例:繼承自 Animal 的 Cat 就必須撰寫(實作) Shout() 這個<font color=Green>抽象方法</font>的程式碼 ![](https://i.imgur.com/Sr4twXA.png) * 補充資料:[[1]](http://notepad.yehyeh.net/Content/CSharp/CH01/03ObjectOrient/7AbstractClass/index.php)[[MSDN]](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/abstract) ::: ### <span class="SubTitle">Lab1-2 藉由 #region 與 #endregion,為程式碼分門別類</span> :::spoiler 實際的效果 ![](https://i.imgur.com/GaPkOv7.png) [完整的程式碼](https://repl.it/@aidanlu/Lab1-2) ::: ### <span class="SubTitle">Lab1-3 要讓 Cat 與 Dog 具備 Run 的能力</span> [執行程式](https://repl.it/@aidanlu/Lab1-3) :::spoiler **Animal Abstract Class 的程式碼** ![](https://i.imgur.com/7vK9BPo.png) ::: ### <span class="Practice">:memo:練習1. 為 Dog Class 新增 hurt 屬性,當狗受傷的時候,就無法跑</span> 執行程式 1. [Version.1](https://repl.it/@jacklouk/Lab1-3#main.cs) 大J 類別圖 ![](https://i.imgur.com/nKcT0b5.png) *** ## <span class="Title">主題2. 玩具貓</span> ### <span class="SubTitle">Lab2-1 建立 ToyCat Class</span> :::spoiler 執行程式 * [修正前](https://repl.it/@aidanlu/Lab2-1) 執行結果 ![](https://i.imgur.com/q3Xy5RD.png) * <span class="Incomplete">[修正後](https://repl.it/@tonyliu13/Lab2-1#main.cs)</span> 執行結果 ![](https://i.imgur.com/sx7JazB.png) ::: :::spoiler 類別圖 ![](https://i.imgur.com/e7BIld9.png) ::: ### <span class="SubTitle">Lab2-2 該是整理程式碼了,一個 class 一個 cs</span> [執行程式](https://repl.it/@aidanlu/Lab2-2) :::info :bulb: 補充說明 * cs 的檔案名稱就是 class name * 把 Animal 一律改為 AAnimal ::: ### <span class="SubTitle">能不能在懶一點(替 interface 的出場暖暖身)</span> > 沒有使用 <font color="#06f">abstract </font>之前的 Animal Class ![](https://i.imgur.com/5FzBW22.png) > > ToyCat Class > ![](https://i.imgur.com/qwKOtIp.png) > :::warning > :warning: 上面兩張圖,都寫了一個沒有實際作用的程式碼 (紅色框框與綠色框框) > ::: > :::info > 假設現在已經擁有一座動物園,就代表已經建立更多的動物類別 (都繼承自 AAnimal Class,例如: class Duck : AAnimal)。 > > :warning: 今天突然要建立一個玩具動物園,那就從每種動類別去延伸 ToyXXX 類別 (例如: class TonyDuck : Duck),那這樣就會有更多沒有實際作用的 Shout() 程式碼 > ::: > ### <span class="SubTitle">Lab2-3 interface 的功用</span> <font color = Red> [執行程式](https://repl.it/@tonyliu13/Lab2-3#main.cs) </font> :::spoiler IBehavior.cs ```C#=+ using System; interface IBehavior { String Shout(); } ``` :::info :key: 用 interface 宣告的介面名稱,習慣上會在最前面冠上 <font color = Purple>**I**</font>,如上方的 <font color = Purple>**I**</font>Behavior ::: :::spoiler AAnimal.cs ```C#=+ using System; abstract class AAnimal { #region Field (欄位) /// <summary> /// 叫 的次數 /// </summary> public int shout_num; /// <summary> /// 名字 /// </summary> public String name; /// <summary> /// 動物的種類 /// </summary> protected string kind; #endregion #region Property (屬性) /// <summary> /// 取得動物的種類,而設定只能在物件建立時決定 /// </summary> public string Kind { get { return kind; } } #endregion #region Constructor (建構函式) /// <summary> /// 無參數的 Constructor /// </summary> public AAnimal() { this.name = "No-Name"; } /// <summary> /// 可以設定 name 與 shout_num 的 Constructor /// </summary> /// <param name="name"></param> /// <param name="shout_num"></param> public AAnimal(String name, int shout_num) { this.name = name; this.shout_num = shout_num; } #endregion #region Methon (方法) //public abstract String Shout(); public virtual String Run(int distance) { return name + $" ran {distance} meter"; } #endregion } ``` :::info :key: Shout() 從 AAnimal Class 移除 ::: :::spoiler Cat.cs ```C#=+ using System; class Cat : AAnimal, IBehavior{ #region Constructor (建構函式) /// <summary> /// 無參數的 Constructor /// </summary> public Cat() { this.kind = "Cat"; this.shout_num = 3; this.name = "No-Name"; } /// <summary> /// 可以設定 name 與 shout_num 的 Constructor /// </summary> /// <param name="name"></param> /// <param name="shout_num"></param> public Cat(String name, int shout_num) : base(name, shout_num) { this.kind = "Cat"; } #endregion #region Methon (方法) /// <summary> /// 要實作 IBehavior 的 Shout() /// </summary> /// <returns></returns> public String Shout() { String result = ""; for (int i = 0; i < shout_num; i++) { result += "meow~ "; } return "My name is " + name + ". " + result; } #endregion } class ToyCat : Cat { #region Constructor (建構函式) /// <summary> /// 可以設定 name 與 shout_num 的 Constructor /// </summary> /// <param name="name"></param> /// <param name="shout_num"></param> public ToyCat(String name) { this.kind = "Toy Cat"; this.name = name; } #endregion #region Methon (方法) #endregion } ``` :::info :key: Cat Class 的宣告變成 class Cat : AAnimal, <font color = Purple>**I**</font>**Behavior** 可以唸做,Cat Class 繼承自 AAnimal,並實作 <font color = Purple>**I**</font>**Behavior** ::: :::spoiler Dog.cs ```C#=+ using System; class Dog : AAnimal, IBehavior{ #region Constructor (建構函式) public bool hurt { get; set; } /// <summary> /// 無參數的 Constructor /// </summary> public Dog() : base() { this.kind = "Dog"; } /// <summary> /// 可以設定 name 與 shout_num 的 Constructor /// </summary> /// <param name="name"></param> /// <param name="shout_num"></param> public Dog(String name, int shout_num) : base(name, shout_num) { this.kind = "Dog"; } #endregion #region Methon (方法) /// <summary> /// 要實作 IBehavior 的 Shout() /// </summary> /// <returns></returns> public String Shout() { String result = ""; for (int i = 0; i < shout_num; i++) { result += "Wong~ "; } return "My name is " + name + ". " + result; } public override String Run(int distance) { if(this.hurt == true) return name + "受傷了, 無法跑"; else return name + $" ran {distance} meter"; } #endregion } ``` :::info :key: Dog Class 的宣告變成 class Dog : AAnimal, <font color = Purple>**I**</font>**Behavior** 可以唸做,Dog Class 繼承自 AAnimal,並實作 <font color = Purple>**I**</font>**Behavior** ::: :::spoiler main.cs(程式有錯,請在下方提出修正) ```C#=+ static void Main(string[] args) { Cat[] cats = new Cat[1]; for (int i = 0; i < cats.Length; i++) { cats[i] = new Cat("阿貓妹" + i, 2); } Dog[] dogs = new Dog[1]; for (int i = 0; i < dogs.Length; i++) { dogs[i] = new Dog("阿狗兄" + i, 5); } ToyCat[] toyCat = new ToyCat[1]; for (int i = 0; i < dogs.Length; i++) { toyCat[i] = new ToyCat($"玩具貓{i}號"); } List<AAnimal> animals = new List<AAnimal>(); animals.AddRange(cats); animals.AddRange(dogs); animals.AddRange(toyCat); foreach (AAnimal animal in animals) { $"AAnimal Kind : {animal.Kind}".ToConsole(); animal.Shout().ToConsole(); animal.Run(5).ToConsole(); Console.WriteLine(); } Console.ReadKey(); } ``` ::: :::spoiler main.cs的正確寫法 :::success ```C#=+ using System; using System.Collections.Generic; class MainClass { public static void Main (string[] args) { Cat[] cats = new Cat[1]; for (int i = 0; i < cats.Length; i++) { cats[i] = new Cat("阿貓妹" + i, 2); } Dog[] dogs = new Dog[1]; for (int i = 0; i < dogs.Length; i++) { dogs[i] = new Dog("阿狗兄" + i, 2); } ToyCat[] toyCat = new ToyCat[1]; for (int i = 0; i < dogs.Length; i++) { toyCat[i] = new ToyCat(); } List<AAnimal> animals = new List<AAnimal>(); animals.AddRange(cats); animals.AddRange(dogs); animals.AddRange(toyCat); foreach (AAnimal animal in animals) { $"Animal Kind : {animal.Kind}".ToConsole(); ((IBehavior)animal).Shout().ToConsole(); animal.Run(5).ToConsole(); Console.WriteLine(); } } } public static class Log { public static void ToConsole(this string msg) { Console.WriteLine(msg); } } ``` ::: :::spoiler 類別圖 ![](https://i.imgur.com/YD7fppb.png) ::: ### <span class="SubTitle">Lab2-4 加入具有飛行能力的 Duck Class</span> <font color = Red>執行程式</font> :::spoiler IBehavior.cs ```C#=+ using System; interface IBehavior { String Shout(); String Fly(); } ``` ::: :::spoiler Duck.cs ```C#=+ using System; class Duck : AAnimal ,IBehavior{ #region Constructor (建構函式) /// <summary> /// 無參數的 Constructor /// </summary> public Duck() { this.kind = "Duck"; this.shout_num = 3; this.name = "No-Name"; } /// <summary> /// 可以設定 name 與 shout_num 的 Constructor /// </summary> /// <param name="name"></param> /// <param name="shout_num"></param> public Duck(String name, int shout_num) : base(name, shout_num) { this.kind = "Duck"; } #endregion #region Methon (方法) /// <summary> /// 要實作 IBehavior 的 Shout() /// </summary> /// <returns></returns> public String Shout() { String result = ""; for (int i = 0; i < shout_num; i++) { result += "呱~ "; } return "My name is " + name + ". " + result; } /// <summary> /// 要實作 IBehavior 的 Fly() /// </summary> /// <returns></returns> public String Fly() { return $"{name} 飛上天了"; } #endregion } ``` ::: <span class="Practice">:memo:練習2. AAnimals.cs, Cat.cs, Dog.cs 與 main.cs 該怎麼改寫呢??</span> ### <span class="SubTitle">Lab2-5 移除 Cat Class 與 Dog Class 的 Fly() <font color = Red>執行程式</font> Cat Class ![](https://i.imgur.com/PIfKHUv.png) Dog Class ![](https://i.imgur.com/4oo9vTk.png) :::spoiler Step1. 將IBehavior.cs拆分為IShoutBehavior.cs與IFlyBehavior.cs IShoutBehavior.cs ```C#=+ using System; interface IShoutBehavior { String Shout(); } ``` IFlyBehavior.cs ```C#=+ using System; interface IFlyBehavior { String Fly(); } ``` ::: :::spoiler Step2. 改寫 Cat Class 與 Dog Class (只有實作 IShoutBehavior) ![](https://i.imgur.com/oPGoz7m.png) ![](https://i.imgur.com/khzsUzw.png) ::: :::spoiler Step3. 改寫 Duck Class (同時實作 IShoutBehavior 與 IFlyBehavior) ![](https://i.imgur.com/4GrE6z5.png) ::: :::spoiler Step4. 改寫 main.cs (引入<font color="#0ff">**is**</font>) ![](https://i.imgur.com/tS9M3BS.png) :::