C 學習筆記-物件導向學習

陳國仁
37 min readNov 28, 2019

最近回歸寫C#後端API的職位重碰程式,買書看OOP觀念,也上網找到很讚的[小山的C#教學]影片,也看過與理解過一遍所有相關影片內容,這一篇主要筆記從[小山的 C# 教學-第14課]開始到[小山的 C# 教學-第38課]的觀看影片內容重點,方便以後的觀念複習與快速提醒與查找~

一、小山的 C# 教學-第14課-物件導向基礎 Class小山的 C# 教學-第15課-Class 簡介(續)
物件的設計圖

編寫物件的程式碼-屬性(Property)與方法(Method)

建立物件與使用物件

小山的重點提示
重點提示
1.物件導向就是要透過物件之間的互動來完成工作
2. 想要建立物件就必須要先定義 class
3. class 主要分成「property 屬性」、「method 方法」兩個部分
4. 「property 屬性」代表 class 的性質,「method 方法」代表 class 的行為與能力
5. method 的格式為「public output型別 method名稱(input型別與名稱)」
6. 想要存取物件,就必須使用 class 的變數
7. 一開始宣告 class 的變數後不會產生任何物件,必須要用「new class()」才能夠產生物件
8. 只要在變數後面加上小數點,就可以存取物件的 property 跟 method 了
例如:「s.StudentID」、「s.Say()」

  1. 有輸出 但沒有接收輸入 的Methtod:public string Say()
  2. 有輸出 也有輸入 的Methtod:public string Talk(Student s)

沒有接受輸入輸出的Method:public void Upgrade()

小山重點提示

1. 如果沒有輸出值的話,就要用 void 代替輸出值
void 代表「沒有任何值」

2. 如果要取得輸入值的話,必須用類似宣告變數的方式宣告輸入值
例如:talk 這個 method 有兩個輸入值,字串 sname 與數字 sgrade
那就要打成 p…. talk(string sname, int sgrade) { 定義 method }

3. 將資料包裝成物件可以幫助重複使用資料

4. 將物件傳入其他物件的 method 中使用就是一種「物件互動」的方式

二、小山的 C# 教學-第16課-Value 與 Reference Type

小山 重點提示

1. C# 型別(Type) 依照存取方式不同一共分為三種:Value Type、Reference Type 與 Pointer Type

2. C# 變數裡的資料都是存放在記憶體(Memory)內的

3.「int (整數)」就是一種 Value Type。它會先在記憶體中尋找一個空間,標記為變數名稱,然後將我們指定的數值存入

4. 上一課建立的 Student Class 則是一種 Reference Type。宣告 Reference Type 的變數時,也會先在記憶體中尋找一個空間,標記為變數名稱,然後裡面則「存放物件的記憶體位置」。

5. 物件必須要透過「new Class名稱()」的方式建立,沒有指向任何物件的 reference(參考) 變數,則存放著「null」。

6. 物件都是存放在一個稱為「Heap(堆疊)」的特殊記憶體區塊

三、小山的 C# 教學-第17課-Constructor 建構子

看到new Student();,想到我們在new一個物件時,Student()是一個Method ,其實也是一個建構子!!!

程式可以從8行,簡化成2行!!!!!!

小山重點提示

1. 所謂「建構子」就是一種特殊的 method,它的特徵有兩個:

(1) 不需要設定 output 型別
(2) method 名稱與 class 名稱相同

2. 如果想要讓某個屬性的值預先設定好,可以在建構子內就指定一個數值給它

3. 建構子也能夠接受 input (輸入值、引數),目的是要用來幫助物件做一些初始的設定

4. 如果建構子需要接受初始值,那必須要在建立物件時給予。

例如:

Student s = new Student(10201, "小山");
5. 為了因應不同的情況,可以在 class 內撰寫多個建構子。就算名稱相同,只要輸入值的數量與型別不同即可。這種作法稱為 Overloaded (詳情請看補充)

6. 如果沒有撰寫建構子,編譯器會自動幫 class 產生一個沒有引數的預設建構子。

7. 如果自己有撰寫建構子,編譯器就不會自動幫 class 產生預設建構子。

補充

Overloaded 多載

並不是只有建構子才有 overloaded ,只要是 method 都有可能會有這樣的性質。如果今天有兩個 method,這兩個 method 的名稱相同,但是 signatures 不同,那就是 overloaded。

那甚麼是 signature 呢?signatures 指的就是引數(輸入值)的種類、數目、排列與 method 名稱,例如下面這兩個 method 他們的變數名稱雖然不同,但是因為種類相同,method 名稱也相同,所以 signatures 就算相同。
Example 1:
void sleep(int time, string s) {....}void sleep(int sleepTime, string sound) {....}
而下面這兩個雖然引數數目相同,但是型別不同,所以 signatures 就不相同
Example 2:
void eat(int numberOfFood) {....}void eat(string foodName) {....}因此我們只需要辨別 signatures ,就可以知道 method 是否有重複。重複的 method 是沒有意義的,因為這種情況下,程式無法得知你到底是要呼叫哪一個 method。如 Ex1 中,C# 會當作沒看到第二個 method,所以你永遠無法呼叫到它。那你又為何要這樣寫呢?

另外一個常見問題是,引數與 method 名稱都相同,但是 output 型別不同算是重複嗎?
答案是,算!請看下面這個例子
Example 3:
int run() {...}void run() {...}如果我今天呼叫 run() 的話,你可以辨別我是要呼叫哪一個 run() 嗎?相信你應該了解了吧?這就是為什麼這樣也算是重複的 method。

四、小山的 C# 教學-第18課-this 變數

小山重點提示

1. 如果程式與到出現相同變數的情況,會自動選擇比較「近」的變數
「近」的意思是指 Scope 範圍較小但是在仍在範圍內的意思

2. this 是一個指向自己物件本身的 Reference Type 的變數

3. 若將「this.」記為「這個物件的」會比較好記

相關資訊連結

微軟官方對於 this 的介紹

五、小山的 C# 教學-第19課-static 修飾字

Static Method(靜態方法),Static 變數(靜態變數),Static Class(靜態類別)

小山答:是的,但是有些狀況下可以不需要,就是 Static Method

不用每次都new一個物件浪費資源,只要在方法的public與int中間,加入"static”,

就不用先new出Class物件,直接能呼叫使用該Class的Method !!(如下圖所示):

Static Method,那~有Static 變數嗎?

有的!

Static Method,有Static 變數,那~有Static Class嗎? 有的!!

但是有一個小細節要注意!!! 就是Static Class無法被new出來!!

小山重點提示

1. 使用了 static 修飾過的變數、method,不需要建立物件就可以直接透過 class 名稱使用

2. static 修飾過的變數是所有同 class 的物件共用的

3. static class 不可以用來建立物件

補充

static 變數在記憶體內的狀態

每一個物件的變數與其相關的資料都會存在物件被分配的記憶體內。那本課提到了 static 變數是所有物件共用的,這個時候可能就會不禁問說:那 static 變數放哪裡?答案是,放在 class 本身所屬的記憶體區塊裡。每個程式啟動的時候,都會分配給每個 class 一塊記憶體使用,裡面放的內容包含 class 的 static 變數以及 class 的程式碼。當然這個部分的實作細節,會隨著不同版本的 .NET Framework 而有差異。

相關資訊連結

微軟官方對於 static 的介紹

六、小山的 C# 教學-第20、21課-借還錢模擬小程式

這樣就不用重複寫2次updateMoney()裡面那二行的程式碼了!!

小山重點提示

1. 在紀錄資料的時候,可以盡量使用 class 包起來。除了方便管理之外,以後需要大量增加也比較容易

2. class 內的「變數」可以想成「屬性」,method 則可以想成「行為」

3. class 可以藉由 constructor 來強迫使用者在建立物件的時候輸入某些參數來初始化

4. class 內可以互相呼叫自己的 method

七、小山的 C# 教學-第22課-Array 陣列

[小細節]陣列其實是一種 物件,System內有一個class名為Array!!

string 其實是一個 class,所以它預設值是 null

盡量不要使用到編號以外的號碼

小山的 重點提示

1. 陣列通常用來存放大量類型相同的資料,如 1000 個整數等等

2. 陣列的宣告方法如下,例如要宣告可以存放 40 個整數的陣列,名為 intArray:

int[] intArray = new int[40];
或是如果預先知道要存入的數值,也可以用下面的方式宣告:
(假設要預先存入 92, 83, 100)

int[] intArray = new int[] {92, 83, 100};
3. 如果要存取陣列內的資料,則需要打「陣列名稱 [ 編號 ]」
例如要存取整數陣列 scores 編號 3 的資料:

scores[3] = 100; // 在編號 3 的位置存入 100
4. 陣列的編號是從 0 開始算起,因此如果陣列大小為 40 ,則可以用的編號是 0 ~ 39

5. 陣列一開始會將每個位置的資料填上預設值,預設值則跟每一格存放的資料型別有關。不同型別的預設值請參考「相關資訊連結」的「預設值表」

6. 如果使用了範圍外的編號,只有在程式執行時才會發現錯誤。因此要特別注意

7. 除了 int, double 這些基本型別,也可以建立物件陣列。只需要將型別名稱替換成 class 名稱即可,例如要宣告大小 20 的 Student class 的陣列可以寫:

Student[] students = new Student[20];
8. 陣列的大小是固定的,在宣告的時候就要決定

9. 陣列也是一種物件

補充

Class 的預設值

基本上,所有 Value Type 的變數的預設值都可以在相關連結的「預設值表」中找到。那麼 Reference Type(指向物件的變數) 的預設值又是甚麼?

所有 Reference Type 的預設值都是 null,也就是說,如果你一開始宣告了一個變數 object,然後還沒有用 new 來產生任何物件。那這個 object 裡面存的值就是 null。因此我們在寫程式的時候,常常可以使用下列這個判斷式來比對 Reference Type 的變數是否有指向任何物件。

if (object == null) // 如果 object 沒有指向任何物件,這個判斷式就會成立
另外,string 其實也是一個 class,所以它預設值也是 null。

Value Type 的陣列與 Reference Type 陣列的結構

在看這個條目之前,請先注意是否理解 Value Type 與 Reference Type 這兩種變數的差別。如果不了解的話,可以先去看「小山的 C# 教學-第16課-Value 與 Reference Type」。

在本課的教學中,我們了解到陣列儲存整數的方法是像下面這張圖。

而我們也學過,當我們建立物件的時候,物件的本體是放在一個名為 Heap(堆疊) 的物件海。然後我們物件的變數也只有紀錄物件本體的位置的而已,這些變數就稱為 Reference Type 的變數。

那這些 Reference Type 的陣列實際上的結構是長甚麼樣子呢?實際上,Reference Type 的陣列裡面所存的,只是一群「指向物件本體」的位址而已。如果我們有一個 class Student,然後我們建立了 Student 的陣列。再用下列的程式碼一一地建立物件,並利用陣列來儲存。

Student[] students = new Student[20]; students[0] = new Student(10001, 小山); students[1] = new Student(10002, 小羊); students[2] = new Student(10003, 元正); students[3] = new Student(10004, 高光); ....
那這些東西實際儲存的狀態就會像下面這張圖(箭頭代表我們可以透過位址去找到被指向的物件)

動態陣列

本課教學提到陣列是固定大小的,而且必須要在宣告時就決定大小。事實上,也有不需要事先指定大小,並且可以動態改變大小的陣列。

在 System.Collectioins 這個 namespace 下有一個 class 叫做 ArrayList。它本身的結構與陣列很類似,但是具有自行動態調整大小的特性。當然使用方法上會有些不同。關於這部分的介紹有網友製作了簡單的說明,有興趣的可以點進去參考。

相關資訊連結

微軟官方(MSDN) 對於陣列的簡介

http://msdn.microsoft.com/zh-tw/library/9b9dty7d%28v=vs.80%29.aspx

MSDN 的 Array class 參考資料

http://msdn.microsoft.com/en-us/library/system.array.aspx

C# Value Type 的預設值表

http://msdn.microsoft.com/zh-tw/library/83fhsxwc%28v=vs.80%29.aspx

某位網友對於 ArrayList 使用方法的簡單說明
《[C#.NET][VB.NET] 一般集合 — ArrayList 類別 排序》

http://www.dotblogs.com.tw/yc421206/archive/2009/01/21/6902.aspx

八、小山的 C# 教學-第23課-Garbage Collection 垃圾回收

小山 重點提示

1. 電腦的記憶體中有一塊空間稱為 Heap 來存放物件的本體

2. 當我們執行以下的程式碼後,電腦就會在 Heap 建立物件

new Student(...);
3. 當我們向下面宣告物件的變數時,只是產生一個用來記住物件地址的 Reference 而已

Student s;
4. C# 的程式執行時,背後有一個垃圾車會默默地檢查 Heap ,並刪除不需要的物件

5. 當一個物件沒有任何 reference 指向(儲存物件的地址)他的時候,就會被判定為不需要的物件

相關資訊連結

小山的 C# 教學-第16課-Value 與 Reference Type

http://slmtsite.blogspot.tw/2013/03/c-16-value-reference-type.html

維基百科 - 垃圾回收

九、小山的 C# 教學-第24課-封裝性 Public vs Private

小山 重點提示

1. Public 會使 Property 與 Method 變成任何人皆可觀看、使用與修改
2. Private 會使 Property 與 Method 變成只有自己皆可觀看、使用與修改
3. 一旦 Property 或 Method 被設為 Private, 那就只有同一個 Class 大括號範圍內的東西才有權限使用
4. C# 不加修飾字的話,就會自動設為「internal」,「internal」簡單地來說,就是只有「在同一個檔案內的地方」才有辦法存取的到

補充

存取權限

為了要保護 class 內的某些東西不被隨意更動,C# 提供控制存取權限的關鍵字。而這些關鍵字,就是本課所教的 public, private。除此之外,C# 還提供如 protected, internal…. 等等關鍵字。protected 將會等到繼承性的教學才會提到,而 internal 則可能會在更之後的時候才會提。現階段,public 與 private 的控制就很夠用了。

相關資訊連結

微軟官方(MSDN) 對於「存取修飾字」的介紹

十、小山的 C# 教學-第25課-Private 的常見用途

privated可以拿來做什麼?
狀況1.有些class的property不想讓人看到

狀況2.有些class的property只想讓人看到,但是不想讓別人可以修改,那就把此property設定成private,然後寫一個public的Method給別人讀就好了(如下圖)

狀況3.讓有些class的property在設定上增加一些限制

就像只開放外面的人用public方法修改HP,而不能直接去存取HP,另外寫成方法做引用,也可以避免在很多地方重複寫很多次與寫錯的錯誤~

小山 重點提示

private 在以下三種狀況能夠使用:

1. 有些 Property 想要隱藏起來,不想讓 class 外的東西能夠隨意存取

像是: Password 密碼
因此可以將 Password 直接設為 private
如果有比對密碼的需求,只需要簡單寫一個 public 的 method 來做比較的工作即可

2. 有些 Property 只想設成唯讀,就是只能看,不能夠修改

像是:Account 帳號
同樣先將 Account 設成 private
然後建立一個 public 的 method 叫做 getAcoount 來讓 class 外的東西取得 Account 的內容

3. 想要讓某些 Property 在設定上有所限制

像是:HP 血量,限制:HP 不可以為負數
首先先將 HP 設為 private
然後建立一個 public 的 method 像是本課所提的 hurt 來間接設定它
如果想要觀看 HP 的數值,只要像 (2) 一樣
建立 getHP 的 method 即可

補充

== & equals(string target)

在本課中,我們有比對兩個字串是否相等。相信有學過 Java 的人,應該會發現一件怪事。為什麼比對字串相等是使用「==」,而不是用「equal(string target)」?

事實上,使用 equals 也正確。但是在 C# 中,只要兩字串內的文字相同,就會參照到同個物件。因此如果使用「==」去比較記憶體位置,也會得到相同的值。這在 Java 裡面則會隨著建立 String 的方式不同而有所變化。

因此要記住,在 Java 內,比較字串相等一定要用「equals」;但在 C# 內,使用「==」也可以用做比較字串相等。

注意:以上畫線內容有誤,以下將作修正 (2013/10/24)

感謝網友「小明」提問,我才注意到這個錯誤

我原本是說「只要文字相同,就會參照到同一個記憶體位置」,這句話其實只對一半。事實上,C# 有維護一個稱為「String Pool」(字串池、字串倉庫) 的記憶體空間。C# 會在你的程式編譯的時候,就先把確定知道內容的字串變數都先檢查過,然後把這些字串的值都存在的 String Pool 裡面,最後讓值應該相同的變數指向同一個物件。

舉個例子來說,下面這段程式碼:
String a = "123"; String b = "123"; 我們不需要執行就知道 a 跟 b 的字串都是 "123",所以 C# 就會讓 a 跟 b 指向同一個 String 物件。

但是如果是下面這種狀況:
String a = "123"; String b = "1"; String c = "23"; // 中間可能有其他程式碼 String d = b + c; 那麼 C# 就無法馬上知道 d 會等於多少,因為第四行程式碼可能離前三行很遠,中間 b, c 遭到修改也沒人知道,所以就算最後 a, d 都是 "123",兩個也都會個別指向不同的物件。

另外還有一種情況:
String a = "ccc"; String b = new String('c', 3); 上面這段程式碼,b 會得到 "ccc",也就是跟 a 一樣的字串。但是因為我們利用 new 這個指令,強迫 C# 一定要建立一個 String 物件,所以就算程式在編譯的時候就知道 a, b 相等,仍會讓他們指向不同物件。

最後,以上所有結果,你們使用「==」去比較,還是會全部得到 ture!為什麼?因為 C# 特別處理了 String 比較的情況!一般來說,如果拿 Reference Type 的東西用「==」比較,確實會比較它們的記憶體位置是否相等。但是 C# 有特別規定,如果被比較的東西是 String 的話,那麼就要比較它們的存的「值」是否相等。不過,我個人建議大家,還是用 equals 來比較字串會安全許多。

另外,如果你們想要比較兩個字串的記憶體位置是否相同,那請使用:
String.ReferenceEquals(Object objA,Object objB); 詳細使用方式可以參考下面的連結。

相關資訊連結

網友 larry nung 對 String Pool 的介紹

http://www.dotblogs.com.tw/larrynung/archive/2011/06/30/30763.aspx

微軟官方(MSDN)對於「==」運算子的介紹 (裡面特別提到 String 比較的方式不同)

http://msdn.microsoft.com/zh-tw/library/53k8ybth%28v=vs.90%29.aspx

微軟官方(MSDN)對於 ReferenceEquals() 使用方法的介紹

十一、小山的 C# 教學-第26課-Get & Set 存取器

小山 重點提示

1. Get & Set 存取器的語法如下:......... // 宣告變數(通常是 public) { get { .... } // 希望變數讀取時執行的程式碼 set { .... } // 希望數值存入時執行的程式碼 }
2. 如果只使用 get 而去除 set 的話,該變數將會變為唯讀

3. 使用 Get & Set 存取器時,並不一定要有對應的 private 變數

4. Get & Set 存取器可以用來即時運算一些平常大家認為是變數的數值,像是 Money

相關資訊連結

微軟官方(MSDN) 對於 get 的介紹

http://msdn.microsoft.com/zh-tw/library/ms228503.aspx

微軟官方(MSDN) 對於 set 的介紹

十二、小山的 C# 教學-第27課-所以到底甚麼是封裝性?

小山 重點提示

1. 所謂的「封裝性」指的就是:「隱藏物件內部的實作細節,只留下讓外人操作的介面(方法)」

2. 封裝性的好處有二:
(1) 保護物件內部的變數
(2) 隱藏實作細節,讓別人使用只需知道使用方法即可

3. 有一種技術稱之為「緊密封裝」,意思就是「把物件內所有 Property 都設成 private,並只提供 public method 或 Get&Set 存取器來供外界操作變數」

補充

存取範圍修飾字

其實之前所教的 public 與 private 就是所謂的「存取範圍修飾字」,也就是只要加上其中一個關鍵字,就可以控制後面東西的存取範圍。而在 C# 裡面,這類的修飾字一共有五個:

public, protected, internal, protectedinternal, private。

第一個跟第五個想必大家都很清楚了,那麼其他三個又是甚麼呢?protected 與 protectedinternal 與下一段「繼承性」有關,這邊暫不作介紹。因此我們今天只討論 internal,它的定義是「存取只限於目前的組件 (assembly)」,好像有點難懂?我們來解釋一下。

不知道大家還記不記得,程式碼轉換成可以執行的檔案需要經過一個「編譯 (Compile)」的動作?而所謂的 internal,其實指的就是「在同一次編譯的所有程式碼之中,任何地方都可以存取」的意思。

假設你現在有兩個 Class, A 跟 B,然後 A 之中有個變數:
internal int number;這個時候,如果你先把 Class A 編譯,並塞入一個程式庫(DLL 檔),然後讓 B 去使用這個程式庫。要是 Class B 這個時候想要使用 Class A 的 number 的話,就會發生錯誤!因為 A 跟 B 不是一起編譯的,這樣就不符合 internal 的要求。所以當你不會單獨把一些 class 先編譯成程式庫,或是程式規模很小的時候,internal 其實就跟 public 沒甚麼兩樣。

最後要注意,有沒有想過如果你沒有加任何存取範圍修飾字的話,那麼那些變數預設會是 public 還是 private 呢?這邊要告訴你,如果是物件的 Property 的話,預設的存取範圍就是 internal 喔!

相關資訊連結

MSDN - 存取範圍層級

http://msdn.microsoft.com/zh-tw/library/ba0a1yw2%28v=vs.90%29.aspx

MSDN - internal

十三、小山的 C# 教學-第28課-繼承性

小山 重點提示

1. 所謂的繼承性,指的就是「物件可以透過繼承,獲得其他物件的屬性與行為」

2. 在 C# 之中,想要使用繼承
只需要在定義 class 時,於 class 名稱後面加上「:」(冒號) 與被繼承的 class 名稱即可

例如我想要讓 class A 繼承 class B,就要寫成
class A : B { ... }
相關資訊連結

微軟 MSDN 對於「繼承」的介紹

十四、小山的 C# 教學-第29課-繼承性(續)

小山 重點提示

1. 在 C# 之中,被繼承的 Class 被稱為「Base Class」(基底類別),繼承的 Class 被稱為「Derived Class」(衍生類別)

2. 除了上一課提到,我們可以透過繼承把多個 Class 重複的程式碼寫在同一個 Class 以減少「Class 之間」的重複程式碼之外,我們也可以利用繼承減少「Class 內部」的重複程式碼

3. 如果我們想讓 Monster 可以攻擊不同的生物,像是村民或其他怪物,那可能需要為不同的生物撰寫不同版本的 Attack:
public void Attack(Villager villager) { .... } public void Attack(Monster monster) { .... } 但是我們都知道 Villager 與 Monster 就繼承自 Creature,所以我們可以這樣寫:
public void Attack(Creature creature) { .... } 這樣只要是 Creature 或是它的衍生類別,都可以被傳入 Attack 處理
這樣就可以大大減少 Class 內部的重複程式碼

補充

Overloaded (多載)

之前已經提過了何謂 Overloaded,可以從下面的連結觀看之前的介紹
http://slmtsite.blogspot.tw/2013/04/c-17-constructor.html

相關資訊連結

微軟 MSDN 對於「繼承」的介紹

十五、小山的 C# 教學-第31課-Override 覆寫

子class繼承父class,子就會獲得父的屬性(欄位)與方法(行為)

[問題]所有生物預設有攻擊能力,所以主角人物、史萊姆、怪物繼承生物,也都有攻擊能力,但是村民繼承生物時,如何只讓村民喪失攻擊能力??

[問題]寫新的method(例如public string attack2())也能夠做到剛剛的事情,那為什麼需要override??

小山答:使用override有另一個很大的好處,就是除了子class 可以呼叫到自己的method之外,有用override的子method,也可以讓父class 呼叫到!!!

[小知識]override好處,可以用父class的變數,來存取子class的物件!!!

[提醒1.]父class(就是class Creature)的methtod前有加virtual,這樣父class的move() 與 attack()才能被override。

[提醒2.]override的 method 其名稱與參數型別都要跟 被override的相同,因為在C#中,有兩個 method,名稱相同,但是 signatures引數不同,那就變成 overloaded多載的情形,也就視為不同method,所以不能override它。

[提醒3.]overloaded多載的另一個常見問題是,引數與 method 名稱都相同,但是 output 型別不同算是重複嗎?
答案是,算!請看下面這個例子
Example:
int run() {...}void run() {...}如果我今天呼叫 run() 的話,你可以辨別我是要呼叫哪一個 run() 嗎?相信你應該了解了吧?這就是為什麼這樣也算是重複的 method。

小山 重點提示

1. Override 指的是「改寫、覆寫」,主要是用來讓繼承的 class 改寫掉從 base class 繼承到的行為。

2. 想要使用 Override,首先必須要先在繼承的 class 中定義一個名稱與參數皆相同的method,然後在原本的 method 前加上「virtual」關鍵字,而新的 method 前加上「override」關鍵字。

補充

Override 使用時機

基本上在撰寫物件導向的程式時,為了要讓許多物件有相同的動作,通常會寫一個 class 包含該動作,並讓其他 class 繼承它。就像是這次課程中介紹的 move 或 attack。可是偏偏會有一些狀況會需要讓某些物件的行為與預設的不同,而當你需要這種修改時,就會用到 override。

另外,Override 也常用於多型性中,主要是因為它可以讓許多具有相同 method 的物件具有不同行為,這部分會在之後的課程中提及。

相關資訊連結

MSDN — 官方對於 Override 的說明

十六、小山的 C# 教學-第32課-Override vs Method Hiding

[觀念複習]A類別有繼承自B類別的話,那麼宣告出來(new出來)的A類別(子類別)的物件,就可以用B類別(父類別)的變數去存取A類別(子類別)的物件。

[舉例]有Apple類別繼承自Fruit類別,則 Fruit父類別變數 f ,
可以用 Fruit f = new Apple(); 去存取Apple子類別的Apple()物件。

A:父類別 B、C:子類別

[Override]B子類別改寫原本的A父類別的method

[Method Hiding]C子類別建立新的method,與舊A父類別存在的virtual method共存,變數是C子類別就會呼叫到新的method,變數是A父類別就會呼叫到舊的method。

十七、小山的 C# 教學-第33課-Protected

權限嚴格程度比較:
Private(全關) > Protected(保護,部分開放,有繼承才能用) >Public(全開)

下面舉一個 Protected 變數 的例子:

顯示結果是500,這代表:

  1. 外部的magicButton_Click()方法內,new建立了史萊姆的物件後,
  2. 成功驗證在史萊姆的public Slime()方法內,已將hp=100 改成hp=500,
  3. 然後透過了史萊姆繼承怪物的public int getHp()方法,
  4. 可以在外部的magicButton_Click()方法內,呼叫到getHp()的方法,然後取得HP數值=500

這就是Protected的作用!!!

Protected使用時機:

用來控管不想讓外人隨意使用,但是又想讓繼承的Class內能存取的資源。

Protected 對變數 可以用,Protected 對 方法 也是可以用!!

  1. 因為把怪物內的 自我介紹introduceSelf()方法設為protected
  2. 所以就只能在繼承怪物的史萊姆Class中去使用introduceSelf()方法,外面其他沒有繼承怪物的Class是沒辦法直接碰觸到的。
  3. 外面其他沒繼承怪物的Class只能透過存取史萊姆的public string say(),才能取得introduceSelf()方法的內容

小山 重點提示

1. protected 與 public 和 private 相同,都是用來進行權限控管的關鍵字

2. 套用 protected 的資源不可以直接從外界存取 (類似 private),但是可以從繼承的 class 之中存取

3. 開放程度:pubilc > protected > private

相關資訊連結

MSDN — protected

十八、小山的 C# 教學-第34課-Base 關鍵字

十九、小山的 C# 教學-第35課-Abstract Class & Abstract Method 抽象類別 與 抽象方法

Abstract 抽象 KK[ˋæbstrækt] 重音在第一音節

Abstract Class的好處,是可以放入一個Abstract Method,

但是!!!

一般Method的組成:
1.宣告
2.內容(實際執行方式)

注意!!!Abstract method 沒有2.內容!!!!!只有1.宣告

但實際上,這種abstract method是沒辦法做事情的,但是為什麼要有不能做事情的method??

當需要強迫 繼承的子類別 實做(override) 父類別的 某些功能,就能使用abstract method!!!

[重點!!]abstract method可以把實作的工作,轉交給繼承的類別來定義!!

另外,因abstract method使繼承的子類別使用了override,也間接擁有了override好處,也就是可以用父class的變數,來存取子class的物件!!!

可以看前面 十五、十六複習:
十五、小山的 C# 教學-第31課-Override 覆寫
十六、小山的 C# 教學-第32課-Override vs Method Hiding

另外找到Allen大對abstract method的論壇回覆文章的解釋:
http://www.blueshop.com.tw/board/FUM20050124192253INM/BRD200903161438213TP.html
abstract class A
interface Ix
class B:A,Ix{…}
上述的code,我稱它為”class B繼承 class A, 並實作Ix介面”

我再列幾個狀況您參考一下
1.如果class B繼承class A,而你不想要求B一定要覆寫什麼A的method,那麼class A是否宣告成abstract class都可以
2.如果class B繼承class A,而你不想要別人可以new A出來, 那麼class A就可以宣告成abstract class, class A裡不必宣告abstract method
3.如果class B繼承class A,而你想要 B一定要寫某些 A的method(有點像B實作Ix的味道),那麼class A就可以宣告成abstract class, 並宣告abstract method, 逼class B一定要寫那些method

另外也找到 劉逸大大的文章作抽象類別的觀念補充:
https://antrash.pixnet.net/blog/post/71078905

抽象類別的適用時機。下面分兩點說明,第一:我想抽象類別的存在就如同上述抽象名詞的概念,我們都希望說的越少但能表示的越多,而抽象化能幫助我們達到此一目標。相對的,在程式的世界中若想讓程式寫的越少卻能涵蓋的越多,那就要善用抽象類別,搭配前面我曾說過的多型一起使用,將可以達到:用越少的程式達到越多的效果。第二:抽象類別中的抽象方法具備了規範的效果,當有一類別繼承了抽象類別,那勢必需要將抽象方法的內容重新定義(我們將此稱為『overriding』),這樣我們可以確保繼承抽象類別的子類別全部都會有抽象類別中的抽象方法重新定義之method。等等~我想讀者已經頭暈了,換個實例說明,Animal是個抽象類別,當要繼承這個抽象類別時,必須把move() overriding,亦即將move的內容定義出來,如果是人那就是走路、獅子就是四條腿移動、魚就是游動,當你沒有重新定義函式,compiler會自動偵測並且回報錯誤來提醒工程師,這樣一來我們可以放心的用Animal這個抽象類別來動態操作下面的子類別囉~~

善用抽象吧,越抽象往往威力越大。能否理解用抽象反轉來操控底層的實體,是衡量一程式設計師是否具備優良的工匠技藝的關鍵指標之一。

--

--