在衍生類別中對虛方法進行重載
先讓我們回顧一下普通的方法重載,普通的方法重載指的是:類中兩個以上的方法(包括隱藏的繼承而來的方法),取的名字相同,只要使用的參數類型或者參數個數不同,編譯器便知道在何種情況下應該調用哪個方法。
而對基類虛方法的重載是函數重載的另一種特殊形式。在衍生類別中重新定義此虛函數時,要求的是方法名稱,傳回值類型、參數表中的參數個數、類型順序都必須與基類中的虛函數完全一致。在衍生類別中聲明對虛方法的重載,要求在聲明中加上override 關鍵字,而且不能有new, static 或virtual 修飾符。
看一個用汽車類的例子來說明多態性的實現的程式:
using System ;
class Vehicle//定義汽車類
{
public int wheels; //公有成員輪子個數
protected float weight; //保護成員重量
public Vehicle(int w,float g)
{
wheels = w;
weight = g;
}
public virtual void Speak( )
{
Console.WriteLine( " the w vehicle is speaking!" ) ;
}
};
class Car:Vehicle //定義轎車類
{
int passengers; //私人成員乘客數
public Car(int w,float g,int p) : base(w,g)
{
wheels = w;
weight = g;
passengers = p;
}
public override void Speak( )
{
Console.WriteLine( " The car is speaking:Di-di!" ) ;
}
}
class Truck:Vehicle //定義卡車類
{
int passengers; //私人成員乘客數
float load; //私人成員載重量
public Truck (int w,float g,int p, float l) : base(w,g)
{
wheels = w;
weight = g;
passengers = p;
load = l;
}
public override void Speak( )
{
Console.WriteLine( " The truck is speaking:Ba-ba!" ) ;
}
public static void Main( )
{
Vehicle v1 = new Vehicle(0,0 ) ;
Car c1 = new Car(4,2,5) ;
Truck t1 = new Truck(6,5,3,10) ;
v1.Speak( ) ;
v1 = c1;
v1.Speak( ) ;
c1.Speak( ) ;
v1 = t1;
v1.Speak( ) ;
t1.Speak( ) ;
}
}
分析上面的例子我們看到:
● Vehicle 類中的Speak 方法被聲明為虛方法,那麼在衍生類別中就可以重新定義此方法。
● 在衍生類別Car 和Truck 中分別重載了Speak 方法,衍生類別中的方法原型和基類中的方法原型必須完全一致。
● 在Test 類中,建立了Vehicle 類的執行個體v1, 並且先後指向Car 類的執行個體c1 和Truck 類的執行個體t1。
運行該程式結果應該是:
The Vehicle is speaking!
The car is speaking:Di-di!
The car is speaking:Di-di!
The truck is speaking:Ba-ba!
The truck is speaking:Ba-ba!
這裡,Vehicle 類的執行個體v1 先後被賦予Car 類的執行個體c1, 以及Truck 類的執行個體t1的值。在執行過程中,v1 先後指代不同的類的執行個體,從而調用不同的版本。這裡v1 的Speak 方法實現了多態性,並且v1.Speak 究竟執行哪個版本,不是在程式編譯時間確定的,而是在程式的動態運行時,根據v1 某一時刻的指代類型來確定的,所以還體現了動態多態性。
四、介面多態性
多個類可實現相同的“介面”,而單個類可以實現一個或多個介面。介面本質上是類需要如何響應的定義。介面描述類需要實現的方法、屬性和事件,以及每個成員需要接收和返回的參數類型,但將這些成員的特定實現留給實作類別去完成。
組件編程中的一項強大技術是能夠在一個對象上實現多個介面。每個介面由一小部分緊密聯絡的方法、屬性和事件組成。通過實現介面,組件可以為要求該介面的任何其他組件提供功能,而無需考慮其中所包含的特定功能。這使後續組件的版本得以包含不同的功能而不會干擾核心功能。
其他開發人員最常使用的組件功能自然是組件類本身的成員。然而,包含大量成員的組件使用起來可能比較困難。可以考慮將組件的某些功能分解出來,作為私下實現的單獨介面。
根據介面來定義功能的另一個好處是,可以通過定義和實現附加介面增量地將功能添加到組件中。優點包括:
● 簡化了設計過程,因為組件開始時可以很小,具有最小功能;之後,組件繼續提供最小功能,同時不斷插入其他的功能,並通過實際使用那些功能來確定合適的功能。
● 簡化了相容性的維護,因為組件的新版本可以在添加新介面的同時繼續提供現有介面。用戶端應用程式的後續版本可以利用這些介面的優點(如果這樣做有意義)。
五、繼承多態性
多個類可以從單個基類“繼承”。通過繼承,類在基類所在的同一實現中接收基類的所有方法、屬性和事件。這樣,便可根據需要來實現附加成員,而且可以重寫基成員以提供不同的實現。請注意,繼承類也可以實現介面,這兩種技術不是互斥的。
C# 通過繼承提供多態性。對於小規模開發工作單位而言,這是一個功能強大的機制,但對於大規模系統,通常證明會存在問題。過分強調繼承驅動的多態性一般會導致資源大規模地從編碼轉移到設計,這對於縮短總的開發時間沒有任何協助。看下面的例子:
class B
{ public virtual void foo () {} }
class D : B
{
public override void foo () {}
}
//試圖重載一個非虛的方法將會導致一個編譯時間錯誤,除非對該方法加上“new”關鍵字,//以指明該方法意欲隱藏父類的方法。
class N : D
{
public new void foo () {}
public static void Main() {
N n = new N ();
n.foo( ) ; // 調用N的foo
((D)n).foo( ) ; // 調用D的foo
((B)n).foo( ) ; // 調用D的foo
}
}
和C++、Java相比,C#的override關鍵字使得閱讀原始碼時可以清晰地看出哪些方法是重載的。不過,使用虛方法有利有弊。第一個有利點是:避免使用虛方法輕微的提高了執行速度。第二點是可以清楚地知道哪些方法會被重載。
我們看一個關于飛機描述的類。假設我們有一個描述飛機的基類。現在,我們要完成一個飛機控制系統,有一個全域的函數fly,它負責讓傳遞給它的飛機起飛,那麼,只需要這樣:
using System ;
class plane
{
public virtual void fly(){} //起飛純虛函數
public virtual void land() {} //著陸純虛函數
public virtual string modal(){} //查尋型號純虛函數
}
// 然後,我們從plane派生出兩個子類,直升機(copter)和噴氣式飛機(jet):
class copter:plane
{
private String fModal ;
public override void fly(){}
public override void land(){}
public override string modal(){}
}
class jet : plane
{
private String fModal ;
public override void fly(){}
public override void land(){}
public override string modal{}
}
就可以讓所有傳給它的飛機(plane的子類對象)正常起飛!不管是直升機還是噴氣機,甚至是現在還不存在的,以後會增加的飛碟。因為,每個子類都已經定義了自己的起飛方式。
可以看到 plane.fly()函數接受參數的是 plane類對象引用,而實際傳遞給它的都是 plane的子類對象,多態性是允許你將父物件設定成為和一個或更多的他的子物件相等的技術,賦值之後,父物件就可以根據當前賦值給它的子物件的特性以不同的方式運作。 很顯然,parent = child; 就是多態的實質!因為直升機“是一種”飛機,噴氣機也“是一種”飛機,因此,所有對飛機的操作,都可以對它們操作,此時,飛機類就作為一種介面。多態的本質就是將子類類型的指標賦值給父類類型的指標(在OP中是引用),只要這樣的賦值發生了,多態也就產生了,因為實行了“向上映射”。