740 likes | 1k Views
Visual C# 2010 程式設計經典. 第 7 章 繼承、多型、介面. 7.1 繼承 7.1.1 類別繼承. 物件導向程式設計中的繼承就類似真實世界的遺傳一樣,例如兒子會遺傳爸爸或媽媽的特色 ( 屬性或方法 ) ,且兒子會再擁有自己新的特色。 透過繼承的機制可以讓新的類別可以延伸出更強的功能,通常我們將被繼承的類別稱為基底類別 (base class) 、父類別 (parent class) 或超類別 (super class) ,而繼承的類別稱為衍生類別 (derived class) 、子類別 (child class) 或次類別 (sub class) 。
E N D
Visual C# 2010 程式設計經典 第7章 繼承、多型、介面
7.1 繼承7.1.1 類別繼承 • 物件導向程式設計中的繼承就類似真實世界的遺傳一樣,例如兒子會遺傳爸爸或媽媽的特色(屬性或方法),且兒子會再擁有自己新的特色。 • 透過繼承的機制可以讓新的類別可以延伸出更強的功能,通常我們將被繼承的類別稱為基底類別(base class)、父類別(parent class)或超類別(super class),而繼承的類別稱為衍生類別(derived class)、子類別(child class)或次類別(sub class)。 • 當子類別繼承自父類別之後,子類別會擁有父類別所有的成員(屬性、方法、欄位),C# 的繼承語法如下:
7.1.2 類別成員的存取限制 • 類別的成員存取修飾詞除了可以使用private和public之外,還可以使用protected,下面我們對這三個常用的修飾詞來加以說明: • publicpublic成員的存取沒有限制,可以在類別中、子類別中或宣告的物件中使用public成員。是屬於公用層級。 • privateprivate成員只能在自身類別內做存取的動作。是屬於私用層級,外界無法使用。 • protectedprotected成員除了可以讓自身類別存取之外,也可以讓子類別做存取。是屬於保護層級。
下面範例定義Employee員工類別有Salary屬性,Salary屬性薪水設定介於20000~40000之間。下面範例定義Employee員工類別有Salary屬性,Salary屬性薪水設定介於20000~40000之間。 • 然後再定義一個繼承自Employee員工類別的Manager經理類別,並在經理類別中新增一個Bonus獎金屬性以及顯示實領薪水的ShowTotal方法。 • 因為Manager經理類別繼承自Employee員工類別,所以Manager類別擁有Employee類別的所有成員(屬性及方法),因此Manager類別也可以使用Salary屬性。 • 完整程式碼如下:
7.2 靜態成員7.2.1 靜態成員的使用 • 在類別中除了上述private、protected、public三種不同等級的成員宣告方式之外,在某些特殊狀況下還可以使用static敘述來宣告「靜態成員」(Static Members)。 • 使用static宣告出來的成員是不需要經過new敘述來建立物件實體就可以直接透過類別來使用的。 • static成員在記憶體中只會儲存一份,且類別所產生的物件都可以一起共用static成員。
例如下面例子,在Car類別中宣告Total靜態屬性用來記錄車子物件的總數,當建立Car物件執行建構式時,Total即馬上加上1;在Car類別定義ShowTotalCars() 靜態方法用來顯示目前共產生幾部車子,呼叫此方法可直接使用Car. ShowTotalCars(); 敘述,不用建立Car物件。 • 完整程式碼如下:
7.3 多型 • 「多型」又稱同名異式,它是透過動態繫結的方式讓您在程式執行時期可以動態決定物件參考所要執行的方法。 • 多型允許您在程式中使用名稱相同的方法或屬性,但不需考慮當時使用的物件型別是什麼。 • 若要設計多型,子類別就必須先覆寫父類別同名稱的方法或屬性,接著再使用父類別的物件參考來選擇所要執行子類別物件實體的方法。 • 由於多型的使用上會在類別中建立名稱相同的成員(屬性或方法),多載和覆寫也可以建立名稱相同的成員,因此有必要釐清這兩者的概念:
多載成員:可接受不同個數的參數或不同資料型別的參數,用來提供不同版本的屬性或方法。多載成員:可接受不同個數的參數或不同資料型別的參數,用來提供不同版本的屬性或方法。 • 覆寫成員:子類別的成員可用來取代父類別中不適用的成員,子類別覆寫父類別的成員時,子類別與父類別的成員必須接受相同個數的參數與相同的資料型別。
7.3.1 覆寫 • 如果子類別想要重新定義父類別的方法或屬性(覆寫),首先必須將父類別的方法宣告為virtual,表示父類別允許被子類別同名的方法覆蓋。 • 而子類別要覆蓋父類別同名稱的方法,必須將子類別的方法宣告為override(覆寫),表示要重新定義父類別的方法。
一般來說員工和經理的底薪一定不相同,在Polymorphism-1.sln範例我們將Employee員工類別的Salary屬性宣告為virtual可被覆寫,且員工Salary薪水屬性介於20000~40000之間。一般來說員工和經理的底薪一定不相同,在Polymorphism-1.sln範例我們將Employee員工類別的Salary屬性宣告為virtual可被覆寫,且員工Salary薪水屬性介於20000~40000之間。 • Manager經理類別的Salary薪水屬性宣告為override來覆寫父類別的Salary屬性,且重新定義Manager經理類別的Salary薪水屬性介於30000~60000。 • 完整程式碼如下:
7.3.2 子類別如何存取父類別的 方法或屬性 • 如果子類別只是要加強父類別方法而己,在子類別方法中並不需要再重新撰寫和父類別方法中相同的程式,只要在子類別的方法中呼叫父類別的方法,然後在子類別方法內加上新的功能即可。 • 在C# 您可以使用base這個關鍵字來呼叫父類別的屬性或方法。寫法如下: base.方法([引數串例]) //呼叫父類別的方法 base.屬性 //呼叫父類別的屬性
下面例子Employee父類別的ShowTotal方法會顯示底薪,而Manager子類別的ShowTotal方法除了要顯示底薪之外,還必須顯示薪水+獎金的總額。下面例子Employee父類別的ShowTotal方法會顯示底薪,而Manager子類別的ShowTotal方法除了要顯示底薪之外,還必須顯示薪水+獎金的總額。 • 因此在Manager類別ShowTotal方法中只需要使用base.ShowTotal來呼叫Employee的ShowTotal方法,然後再加上要顯示薪水+獎金的程式敘述就可以了。
7.3.3 動態繫結 • 一般程式編譯階段時,物件參考即會決定自己要執行的方法。 • 動態繫結(Dynamic Binding)是程式執行階段時物件參考才決定所要執行的方法,其做法是使用父類別的物件參考來選擇所要執行子類別物件實體的方法,透過這種技巧才可以做到真正的多型。
下面這個範例我們模擬使用者可以選擇駕駛車子及飛機共前進幾公里。下面這個範例我們模擬使用者可以選擇駕駛車子及飛機共前進幾公里。 • 範例中Traffic交通工具類別中包含_miles共用成員用來記錄已前進的公里數,且還有一個空的SpeedUp加速方法,子類別Car和Airplane類別分別繼承了Traffic(車子和飛機都是交通工具)且覆寫了Traffic父類別SpeedUp方法,Car類別的SpeedUp方法每一次加速是前進2公里;Airplane類別的SpeedUp方法每一次加速是前進15公里。 • 在Main() 方法中設定一個無窮迴圈讓使用者選擇要駕駛車子或飛機。 • 若按 “1” 選擇車子即將Traffic的參考物件r指向Car的myCar物件實體,此時執行r.SpeedUp()即會呼叫Car類別的SpeedUp方法。 • 若按 "2" 選擇飛機即將Traffic的參考物件r指向Airplane的myAirplane物件實體,此時執行r.SpeedUp()會呼叫Airplane的SpeedUp方法。
7.3.4 抽象類別 • 抽象類別可定義抽象方法或存取子,最常應用的地方就是抽象類別中的某些功能可以繼承給子類別(衍生類別),然後再由子類別對所繼承的抽象類別中的抽象方法或存取子進行實作,若要宣告抽象類別、抽象方法可以使用abstract 修飾詞。 • 若使用 abstract 修飾詞宣告方法或屬性,即表示此方法或屬性並沒有包含實作的部分。例如:以下敘述的Answer方法沒有程式主體,只有方法宣告及「( )」和「;」結束而己。public abstract void Answer(); • 使用abstract來定義抽象類別及抽象方法,而抽象方法不包含實作程式碼,因此必須在繼承的子類別中以override(覆寫)修飾詞定義新的方法功能。
[多型-範例一]建立一個Windows Form應用程式專案「Polymorphism-4」,並且將所有的類別定義全部放在Class1.cs類別檔中,操作步驟如下:
這個範例的製作原理如下: • 如果要利用多型的特性來做,勢必要宣告出一個物件變數的參考(Reference),然後在各個控制項的Click事件中再動態繫結到對應的控制項變數去,然後再來將Left屬性加上10就可以了,問題是這些控制項都繼承自哪個類別呢?沒錯!所有 .NET Framework中的控制項都繼承自Control類別,所以我們可以宣告一個Control類別的物件變數參考(Reference),變數名稱為objToMove,其寫法如下:Control objToMove;
7.4 介面與實作 • 而介面是物件的某些操作集合。 • 例如汽車可以有駕駛的介面 (左轉、右轉、加速、減速),對於駕駛人來說,只要任何交通工具具有駕駛的介面,就可以駕駛它 (多型),但是對於加油站的加油員來說,只要任何交通工具有加油的介面 (開油箱蓋、加油、關油箱蓋),他都可以為它加油 (多型),因此汽車由於具有加油的介面,所以到加油站去加油員可以為它加油。 • 因此介面的使用焦點是放在操作物件這件事上,而類別繼承則是將焦點放在屬性與方法的重覆使用這件事上。 • 參考http://netgo2oo.blog.ithome.com.tw/post/224/805
一. 如何使用介面 • 介面和類別很像,類別可以定義屬性、方法和事件,但介面和類別不同的是,介面只宣告方法、屬性和事件成員,且介面所宣告的成員皆會自動成為public公用成員。 • 介面最主要是用來宣告一組可操作的方法,它就代表一種方法的合約,當類別實作(Implements)某介面之後,該介面所宣告的方法要在類別重新實作過。 • 例如Car本身擁有SpeedUp方法,現在想再加上Fly方法,首先可以使用interface關鍵字定義IFly介面,該介面內宣告Fly方法,其寫法如下: interface IFly // 定義IFly介面 { void Fly(int n); // 宣告Fly方法 }
接著在Car類別就可以使用「:」符號來指定要實作IFly介面,並為Car類別加上Fly() 方法。其寫法如下: class Car : IFly //Car類別實作IFly介面 { public void SpeedUp(int n) { Console.WriteLine("車子加速前進 {0} 公里", n); } //Car類別的Fly方法實作IFly介面的Fly方法 public void Fly(int n) { Console.WriteLine("車子飛上天前進 {0} 公里", n); } }
下面InterfaceDemo.sln範例,在Car類別具有SpeedUp() 加速方法,Bird類別具有Eat() 吃的方法,若希望Car類別和Bird類別同時擁有Fly() 飛的方法,在這裡定義IFly介面內宣告Fly() 方法,一旦Car類別和Bird類別實作IFly介面的Fly() 方法,此時Car類別和Bird類別的物件即可以呼叫Fly() 方法。
Array類別中有一個Sort方法,可以用來排序一維陣列的資料,例如排序一個整數陣列:Array類別中有一個Sort方法,可以用來排序一維陣列的資料,例如排序一個整數陣列: • 但是如果想要排序非內建型別 (int, long …) 的陣列,Sort方法就不支援了,這時候該怎麼辦呢? int[ ] Scores = new int[ ] {89, 65, 31, 89, 92, 46} ; Array.Sort(Scores);