###### tags: `C#`
# 抽象類別 (Abstract Class)
## 說明
抽象(abstract)通常表示一種想法、意念,而非真正的實體,當一個類別使用關鍵字 **abstract** 宣告,表示其為抽象類別,提供其他衍生類別共同的製作樣版。例如建築公司根據房屋樣版模型建造房屋,而各種房屋均擁有出入大門、各式起居房間、衛浴等共同的設施,但是門如何開、房間如何裝潢,則於建造完成進行裝潢時依據屋主需求而定。
抽象類別正是這樣的概念,就如同你無法住在只建好外觀的房屋裏是一樣的,**抽象類別本身無法產生實體物件,而且抽象類別包含了一個以上的抽象方法,這些方法只提供函式名稱與參數設定,並且由繼承的衍生類別實作,衍生類別同時必須實作所有抽象類別的方法,否則其本身將成為另一個抽象類別。**

左邊的抽象類別 A 以虛線作表示,由於抽象類別並沒有完整的實作內容,因此無法建立成為物件實體,右邊則是繼承抽象類別的實體類別 B ,其中實作了抽象類別所定義的抽象成員`aMethod` 。
類別 B 由於完成了抽象成員`aMethod`的實作,因此本身是一個完整的類別,除了可以建立物件,同時可根據需求,進一步擴充其內容,就如同一般的類別。
## 定義抽象類別
抽象類別由 **abstract** 關鍵字定義,底下為定義抽象類別的語法:
```csharp=
abstract class TemplateClass
```
## 定義抽象方法
抽象方法同樣必須以 **abstract** 關鍵字作宣告:
```csharp=
abstract type method(parameter)
```
## 實作抽象方法
**抽象方法本身只有定義,並且由繼承抽象類別的衍生類別進行實作,也就是在衍生類別當中覆寫這些抽象方法成員,就如同覆寫一般類別的虛擬(virtual)成員。**
抽象成員由於必須被衍生類別所覆寫,因此它本身即是一個具有`virtual`性質的成員,在衍生類別當中,覆寫這些抽象成員的實作方式與覆寫一般類別的`virtual`成員相同,同樣必須使用以下的敘述:
```csharp=
override type method(parameter)
```
## 範例
### 建立抽象類別
以下是一個實際的抽象類別,模擬各種幾何形狀的邊長、面積與體積的計算功能,提供作為其他類別所需的基礎架構。
```csharp=
abstract class TemplateClass
{
//變數 len 代表所要計算的各種形狀邊長
public double len = 0;
//建構式接受兩個參數,第一個參數 shape 為形狀種類名稱,第二個參數 r 為邊長
//物件建立時,根據所接受的參數,輸出目前所要測量的形狀,並且將邊長的參數值指定給類別層級變數 len
public TemplateClass(string shape, double r)
{
this.TheShape(shape);
this.len = r;
}
private void TheShape(string shape)
{
Console.WriteLine("目前測量的形狀為{0} ", shape);
}
//實體方法 GetResult 執行類別中三個周長、面積與體積計算功能的相關方法
public void GetResult()
{
Length(len);
Area(len);
Volume(len);
}
//Length、Area 與 Volume,這三個抽象方法,由衍生類別進行實作
public abstract void Length(double len);
public abstract void Area(double len);
public abstract void Volume(double len);
}
```
### 繼承抽象類別
接下來設計兩個繼承抽象類別的衍生類別,分別是`Square`與`Globe`,這兩個類別均引用`TemplateClass`類別的建構式,然後分別實作圓形與正方形的周長、面積以及體積的計算方法。
1. `Square`類別繼承`TemplateClass`,實作所有抽象方法成員,並且透過 **base** 關鍵字引用基礎類別的建構式。
> [base 教學](https://www.youtube.com/watch?v=1eAGN-NPBXE&list=WL&index=3)
```csharp=
class Square : TemplateClass
{
//引用 TemplateClass 類別的建構式
public Square(string shape, double len) : base(shape, len)
{ }
//利用 override 關鍵字,覆寫基礎類別的抽象方法 Length
//實作正方形周長計算方法
protected override void Length(double len)
{
double squareLength = 4 * len;
Console.WriteLine($"邊長為 {len} 的正方形周長 = {squareLength}");
}
//利用 override 關鍵字,覆寫抽象方法 Area
//實作正方形面積計算方法
protected override void Area(double l)
{
double area = Math.Pow(len, 2);
Console.WriteLine($"邊長為 {len} 的正方形面積 = {area}");
}
//利用 override 關鍵字,覆寫抽象方法 Volume
//實作正方形體積計算方法
protected override void Volume(double l)
{
double volume = Math.Pow(len, 3);
Console.WriteLine($"邊長為 {len} 的立方體體積 = {volume}");
}
}
```
2. `Globe`類別繼承`TemplateClass`,實作所有抽象方法成員,並且透過 **base** 關鍵字引用基礎類別的建構式。
```csharp=
class Globe : TemplateClass
{
public Globe(string shape, double r) : base(shape, r)
{ }
protected override void Length(double r)
{
double globeLength = 4 * Math.PI * r;
Console.WriteLine($"半徑為 {r} 的圓形周長 = {globeLength}");
}
protected override void Area(double r)
{
double area = Math.PI * Math.Pow(r, 2);
Console.WriteLine($"半徑為 {r} 的圓形面積 = {area}");
}
protected override void Volume(double r)
{
double volume = (4/3) * Math.PI * Math.Pow(r, 3);
Console.WriteLine($"半徑為 {r} 的球體體積 = {volume}");
}
}
```
3. 執行正方形和圓形周長、面積以及體積的計算方法
```csharp=
class Program
{
static void Main(string[] args)
{
const double len = 10;
//建立 Square 的實體物件
Square mySquare = new Square("正方形", len);
mySquare.GetResult();
Console.WriteLine();
//建立 Globe 的實體物件
Globe myGlobe = new Globe("圓形", len);
myGlobe.GetResult();
Console.ReadLine();
}
}
```
4. 執行結果
```htmlembedded=
目前測量的形狀為 正方形
邊長為 10 的正方形周長 = 40
邊長為 10 的正方形面積 = 100
邊長為 10 的立方體體積 = 1000
目前測量的形狀為 圓形
半徑為 10 的圓形周長 = 125.663706143592
半徑為 10 的圓形面積 = 314.159265358979
半徑為 10 的球體體積 = 3141.59265358979
```
> 參考資料
* [抽象類別](http://www.kangting.tw/2018/11/blog-post.html)
* [小山的 C# 教學-Abstract Class & Abstract Method](https://www.youtube.com/watch?v=i1Mnyu39ns4&list=WL&index=4)