visual c# 2010 程式設計經典

74
Visual C# 2010 程程程程程程 程7程 程程 程程程 程程 、、

Upload: ayla

Post on 17-Jan-2016

59 views

Category:

Documents


0 download

DESCRIPTION

Visual C# 2010 程式設計經典. 第 7 章 繼承、多型、介面. 7.1 繼承 7.1.1 類別繼承. 物件導向程式設計中的繼承就類似真實世界的遺傳一樣,例如兒子會遺傳爸爸或媽媽的特色 ( 屬性或方法 ) ,且兒子會再擁有自己新的特色。 透過繼承的機制可以讓新的類別可以延伸出更強的功能,通常我們將被繼承的類別稱為基底類別 (base class) 、父類別 (parent class) 或超類別 (super class) ,而繼承的類別稱為衍生類別 (derived class) 、子類別 (child class) 或次類別 (sub class) 。 - PowerPoint PPT Presentation

TRANSCRIPT

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,下面我們對這三個常用的修飾詞來加以說明:

1. publicpublic成員的存取沒有限制,可以在類別中、子類別中或宣告的物件中使用 public成員。是屬於公用層級。

2. privateprivate成員只能在自身類別內做存取的動作。是屬於私用層級,外界無法使用。

3. protectedprotected成員除了可以讓自身類別存取之外,也可以讓子類別做存取。是屬於保護層級。

下面範例定義 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.2.2 .NET Framework的記憶體配置

7.3 多型 「多型」又稱同名異式,它是透過動態繫結的方式讓您在程式執行時期可以動態決定物件參考所要執行的方法。

多型允許您在程式中使用名稱相同的方法或屬性,但不需考慮當時使用的物件型別是什麼。

若要設計多型,子類別就必須先覆寫父類別同名稱的方法或屬性,接著再使用父類別的物件參考來選擇所要執行子類別物件實體的方法。

由於多型的使用上會在類別中建立名稱相同的成員(屬性或方法 ),多載和覆寫也可以建立名稱相同的成員,因此有必要釐清這兩者的概念:

多載成員:可接受不同個數的參數或不同資料型別的參數,用來提供不同版本的屬性或方法。

覆寫成員:子類別的成員可用來取代父類別中不適用的成員,子類別覆寫父類別的成員時,子類別與父類別的成員必須接受相同個數的參數與相同的資料型別。

7.3.1 覆寫 如果子類別想要重新定義父類別的方法或屬性 (覆寫 ),

首先必須將父類別的方法宣告為 virtual,表示父類別允許被子類別同名的方法覆蓋。

而子類別要覆蓋父類別同名稱的方法,必須將子類別的方法宣告為 override(覆寫 ),表示要重新定義父類別的方法。

一般來說員工和經理的底薪一定不相同,在 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方法除了要顯示底薪之外,還必須顯示薪水 +獎金的總額。

因此在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方法,可以用來排序一維陣列的資料,例如排序一個整數陣列:

但是如果想要排序非內建型別 (int, long …) 的陣列,Sort方法就不支援了,這時候該怎麼辦呢?

int[ ] Scores = new int[ ] {89, 65, 31, 89, 92, 46} ;Array.Sort(Scores);

在 Array類別中引用了一個 IComparable的介面 (介面的名稱前面一般都以一個大寫的 I來表示 ),在這個介面中定義了一個 CompareTo的方法,用來比較物件的大小:

CompareTo方法的規則是:如果引用 IComparable介面的物件比 obj參數小,則傳回一個負數;如果相等則傳回 0;如果大於 obj參數,則傳回一個正數。

interface IComparable{ int CompareTo(Object obj) ;}

在 Array.Sort方法中進行排序動作時,會呼叫 CompareTo這個方法來比較陣列中兩個元素的大小。

因此,只要我們實作出 CompareTo這個方法來讓 Array.Sort方法呼叫,那麼就可以排序自定型別的資料了。

在以下的這個範例中,為了達到上述的目的,我們定義了一個 Vector的類別,並且在類別宣告時指名在這個類別中要實作出 IComparable介面:

class Vector : IComparable

然後在類別中定義 CompareTo方法,並且指明這個方法就是用來實作出 IComparable介面中的 CompareTo方法的實體:

int IComparable.CompareTo(object obj){

Vector v = (Vector) obj; return (X * X + Y * Y) - (v.X * v.X + v.Y * v.Y);

}

也就是說,經上面兩個步驟的處理,就可以讓我們的Vector類別變成一個可以支援 IComparable介面的類別,也就是說這樣就可以讓 Array.Sort方法在進行排序的時候,呼叫 Vector 陣列 (vecArray) 中每一個 Vector物件中的 CompareTo方法來進行排序:

Array.Sort(vecArray);

三 . 如何使用介面 – 模擬 System.Array類別 上一個範例中,我們僅僅了解執行 Array.Sort(vecArra

y)時, Sort方法可以呼叫 vecArray 陣列中每一個元素的 CompareTo方法來進行元素大小比較,然後進行排序,然而在 Sort方法中究竟是如何做的呢?

為了再深入了解介面的定義方式,我們就來模擬 System.Array物件中有關 Sort方法的技術。

在下面的程式碼中,定義了一個 IMyComparable介面,這個介面中僅有一個方法 ( 函式 )MyCompareTo,方法的規格和 IComparable介面的 CompareTo方法一模一樣,另外也比照 Array類別定義了一個MyArray類別,這個類別中只有一個 Shared方法MySort,在MySort中使用最簡單的汽泡排序法來針對參數 obj 陣列進行排序,在比較陣列元素大小時,就直接呼叫MyCompareTo方法來做比較,因此要做為MySort方法參數的 obj物件 (Vector類別 ) 一定要事先實作出 IMyComparable介面的MyCompareTo方法才可以。

7.5 delegate 委派型別 委派是用來儲存方法位址的資料型別。資料型別可以用來宣告變數或物件,例如 int 型別宣告出來的變數可以儲存數字、 string型別宣告出來的變數可以儲存文字。委派也是一種資料型別,只是這種資料型別宣告出來變數所儲存的是方法的位址,一個儲存了方法位址的委派變數,可以用來呼叫該位址的方法,由於變數的內容可以動態改變,因此只要將該變數內容改變為另一個方法的位址,該變數呼叫的就會是另一個方法。在定義物件的事件時,由於無法預期未來使用這個物件事件的事件處理函式 (即方法 )的位址,因此在物件中就必須使用委派來宣告事件變數,以便可以動態的將事件變數指向事件處理函式,物件就可以動態使用事件的變數來呼叫外部的事件處理函式。

因此方法 ( 函式 )也可以當做一種型別,例如在陣列排序的例子中,如果我們希望 MySort方法排序的方式可以由大排到小,也可以由小排到大,因此在MySort方法中多加一個參數 CompareMethod,指明比較陣列元素大小的方法,由於 CompareMethod參數的型別是一種方法 ( 函式 ),這時候就可以使用 delegate來定義方法的型別 (參數個數、參數順序、傳回值型別 ),首先必須先使用 delegate敘述定義出方法的格式與型別名稱 CompareFunc:

delegate bool CompareFunc(int X, int Y);

將來這個 CompareFunc就可以當成型別來做參數或變數宣告,例如:

這樣MySort中就可以直接呼叫傳進來的 CompareMethod方法來進行陣列元素大小的比較:

static void MySort(int[] obj, CompareFunc CompareMethod)

if (CompareMethod(obj[j], obj[i])) {…}

這種型別的變數其實就是指向函式或方法的位址,例如可以用以下程式來取得 IsSmaller方法的位址:

也就是說經過上述程式取得 IsSmaller方法的位址之後,下面這兩行指令其實都是呼叫 IsSmaller方法:

CompareFunc CompareIt;CompareIt = new CompareFunc(IsSmaller);

if (IsSmaller(15, 20)) { … }if (CompareIt(15, 20)) { … }

7.6 視窗應用程式 在第五章使用「Windows Form 應用程式」專案並透過拖拉的方式來建立表單及控制項的配置與事件處理函式,在本節將介紹使用「主控台應用程式」來建立Windows Form 應用程式。

在 .NET Framework的類別程式庫是以類別繼承架構而來,因此我們可以自行設計一個類別,然後繼承自System.Windows.Forms.Form類別,該類別即擁有表單的屬性及方法,接著您再自行延伸所要的功能即可。