1. 程式人生 > >子類與父類之間的方法過載、隱藏、重寫與虛方法呼叫

子類與父類之間的方法過載、隱藏、重寫與虛方法呼叫

由於子類物件同時“彙集了”父類和子類的所有公共方法,如果子類中某個方法與父類方法的簽名一樣(即方法名和方法引數都一樣),那當通過子類物件訪問此方法時,訪問的是子類還是父類所定義的方法?
總的來說,子類方法與父類方法之間的關係可以概括為以下三種。

    擴充(Extend):子類方法,父類沒有;

    過載(Overload):子類有父類的同名函式,但引數型別或數目不一樣;

    完全相同:子類方法與父類方法從方法名稱到引數型別完全一樣。

對於第一種“擴充”關係,由於子類與父類方法不同名,所以不存在同名方法呼叫的問題,重點分析一下後兩種情況.
一、過載
和普通的方法過載一樣,在同一個類中構成過載的方法主要根據引數列表來決定呼叫哪一個。
class Parent
{
  public void OverloadF()
  {
  }
}
class Child:Parent
{
  public void OverloadF(int i)
  {
  }
}
使用程式碼如下:
Child obj = new Child();
obj.OverloadF(); //呼叫父類的過載方法
obj.OverloadF(100);//呼叫子類的過載方法

可以看到,雖然過載的方法分佈在不同的類中,但仍然可以將其看成是定義在同一個類中的,其使用方式與呼叫類的其他方法並無不同。

二、隱藏(Hide)
class Parent
{
  public void HideF()
  {
    System.Console.WriteLine("Parent.HideF()");
  }
}
class Child : Parent
{
  public void HideF()
  {
    System.Console.WriteLine("Child. HideF()"); //當子類與父類擁有完全一樣的方法時,稱“子類隱藏了父類的同名方法”
  }
}
注:Visual Studio 在編譯這兩個類時,會發出一個警告: “HideExamples.Child.HideF()”隱藏了繼承的成員“HideExamples.Parent.HideF()”。如果是有意隱藏,請使用關鍵 new。 Child c = new Child();
c.HideF();//呼叫父類的還是子類的同名方法?
上述程式碼執行時,輸出:
Child.HideF()
修改一下程式碼:
Parent p = new Parent();
p.HideF();//呼叫父類的還是子類的同名方法?
上述程式碼執行結果:
Parent.HideF()
由此可以得出一個結論:
當分別位於父類和子類的兩個方法完全一樣時,呼叫哪個方法由物件變數的型別決定。
然而,面向物件的繼承特性允許子類物件被當成父類物件使用,這使問題複雜化了,請讀者看以下程式碼,想想會出現什麼結果?
Child c = new Child();
Parent p;
p = c;
p.HideF();//呼叫父類的還是子類的同名方法?
上述程式碼的執行結果是:
Parent.HideF()
這就意味著即使Parent 變數p 中實際引用的是Child 型別的物件,通過p 呼叫的方法還是Parent 類的!
如果確實希望呼叫的子類的方法,應這樣使用:
((Child)p).HideF();
即先進行強制型別轉換。
由於子類隱藏了父類的同名方法,如果不進行強制轉換,就無法通過父類變數直接呼叫子類的同名方法,哪怕父類變數引用的是子類物件。
雖然上述警告並不影響程式執行結果,卻告訴我們程式碼不符合C#的語法規範,修改Child 類的定義如下:
class Child : Parent
{
public new void HideF()
{
System.Console.WriteLine("Child.HideF()");
}
}
“new”關鍵字明確告訴C#編譯器,子類隱藏父類的同名方法,提供自己的新版本。
由於子類隱藏了父類的同名方法,所以如果要在子類方法的實現程式碼中呼叫父類被隱藏的同名方法,請使用base 關鍵字,示例程式碼如下:

base.HideF(); //呼叫父類被隱藏的方法

三、重寫(override)與虛方法呼叫
我們希望每個物件都只幹自己職責之內的事,即如果父類變數引用的是子類物件,則呼叫的就是子類定義的方法,而如果父類變數引用的就是父類物件,則呼叫的是父類定義的方法。
為達到這個目的,可以在父類同名方法前加關鍵字virtual,表明這是一個虛方法,子類可以重寫此方法:即在子類同名方法前加關鍵字override,表明對父類同名方法進行了重寫。
class Parent
{
  public virtual void OverrideF()
  {
    System.Console.WriteLine("Parent.OverrideF()");
  }
}
class Child : Parent
{
  public override void OverrideF()
  {
    System.Console.WriteLine("Child.OverrideF()");
  }
}
請看以下使用程式碼:
Child c = new Child();
Parent p;
p = c;
p.OverrideF();//呼叫父類的還是子類的同名方法?
上述程式碼的執行結果是:
Child.OverrideF()
這一示例表明,將父類方法定義為虛方法,子類重寫同名方法之後,通過父類變數呼叫此方法,到底是呼叫父類還是子類的,由父類變數引用的真實物件型別決定,而與父類變數無關!
換句話說,同樣一句程式碼:
p.OverrideF();
在p 引用不同物件時,其執行的結果可能完全不一樣!因此,如果我們在程式設計時只針對父類變數提供的對外介面程式設計,就使我們的程式碼成了“變色龍”,傳給它不同的子類物件(這
些子類物件都重寫了父類的同名方法),它就幹不同的事。
這就是面嚮物件語言的“虛方法呼叫(Virtual Method Invoke)”特性。
很明顯,“虛方法呼叫”特性可以讓我們寫出非常靈活的程式碼,大大減少由於系統功能擴充和改變所帶來的大量程式碼修改工作量。
由此給出以下結論:
面嚮物件語言擁有的“虛方法呼叫”特性,使我們可以只用同樣的一個語句,在執行時根據物件型別而執行不同的操作。