---
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>
下圖紅色框框處的程式碼並沒有實際的作用

### <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>的程式碼

* 補充資料:[[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://repl.it/@aidanlu/Lab1-2)
:::
### <span class="SubTitle">Lab1-3 要讓 Cat 與 Dog 具備 Run 的能力</span>
[執行程式](https://repl.it/@aidanlu/Lab1-3)
:::spoiler **Animal Abstract Class 的程式碼**

:::
### <span class="Practice">:memo:練習1. 為 Dog Class 新增 hurt 屬性,當狗受傷的時候,就無法跑</span>
執行程式
1. [Version.1](https://repl.it/@jacklouk/Lab1-3#main.cs) 大J
類別圖

***
## <span class="Title">主題2. 玩具貓</span>
### <span class="SubTitle">Lab2-1 建立 ToyCat Class</span>
:::spoiler 執行程式
* [修正前](https://repl.it/@aidanlu/Lab2-1)
執行結果

* <span class="Incomplete">[修正後](https://repl.it/@tonyliu13/Lab2-1#main.cs)</span>
執行結果

:::
:::spoiler 類別圖

:::
### <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

>
> ToyCat Class
> 
> :::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 類別圖

:::
### <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

Dog Class

:::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)


:::
:::spoiler Step3. 改寫 Duck Class (同時實作 IShoutBehavior 與 IFlyBehavior)

:::
:::spoiler Step4. 改寫 main.cs (引入<font color="#0ff">**is**</font>)

:::