FROM P125
在上一節中看到,當使用基類引用訪問派生類對象時,得到的是基類的成員。虛方法可以使基類的引用訪問“升至”派生類內。
可以使用基類引用調用派生類(derived class)的方法,只需滿足下列條件:
□派生類的方法和基類的方法有相同的簽名和返回類型
□基類的方法使用virtual標注
□派生類的方法使用override標注
使用方法如下例:
1 class MyBaseClass //基類 2 { 3 virtual public void Print(); 4 } 5 class MyDerivedClass : MyBaseClass //派生類 6 { 7 override public void Print() 8 }
與上一節中不同,使用基類引用調用Print方法時,方法調用被傳遞到派生類並執行,因為:
□基類的方法被標記為virtual
□派生類中有匹配的override方法
如下圖所示,顯示了一個從virtual Print方法后面開始,並指向override Print方法的箭頭

具體示例如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace review 8 { 9 class MyBaseClass //基類 10 { 11 virtual public void Print() 12 { 13 Console.WriteLine("This is the base class."); 14 } 15 } 16 class MyDerivedClass : MyBaseClass //派生類 17 { 18 override public void Print() 19 { 20 Console.WriteLine("This is the derived class."); 21 } 22 } 23 class Program 24 { 25 static void Main(string[] args) 26 { 27 MyDerivedClass derived = new MyDerivedClass(); 28 MyBaseClass mybc = (MyBaseClass)derived; //強制轉換成基類 29 derived.Print(); 30 mybc.Print(); 31 Console.Read(); 32 } 33 } 34 } 35 /* 36 * 輸出如下: 37 * This is the derived class. 38 * This is the derived class. 39 * */
其他的一些關於virtual和override的信息有:
□覆寫和被覆寫的方法必須有相同的可訪問性。e.g.不能是 被覆寫是private 而覆寫的是public
□不能覆寫static方法或非虛方法
□方法、屬性、索引器,以及另一種成員類型事件,都可以被聲明為virtual和override
Part2 覆寫標記為override的方法:
覆寫方法可以在繼承的任何層次中出現
□當使用對象基類部分的引用調用一個覆寫的方法時,方法的調用被沿派生層次上溯執行,一直到標記為override的方法的最高派生(most-derived)版本。
□如果在更高的派生級別有該方法的其他聲明,但沒有被標記為override,那么它們不會被調用
以以下實例來說明,其中三個類構成一個繼承的層次:
MyBaseClass、MyDerivedClass和SecondDerived。其均包含名稱為Print的方法,並帶有相同的簽名。且分別被標記為virtual、override、 override/new 分別看一下第三個類標記為這兩個的結果。
1 class MyBaseClass //基類 2 { 3 virtual public void Print() 4 { 5 Console.WriteLine("This is the base class."); 6 } 7 } 8 class MyDerivedClass : MyBaseClass //派生類 9 { 10 override public void Print() 11 { 12 Console.WriteLine("This is the derived class."); 13 } 14 } 15 class SecondDerived : MyDerivedClass //最高派生類 16 { 17 ...// Given in the following pages 18 }
1.情況1:使用override聲明Print
如果把SecondDerived的Print方法聲明為override,那么它會覆寫方法的全部兩個低派生級別的版本,如下圖所示,如果一個基類的引用被用於調用Print,它會向上傳遞通過整個鏈達到類SecondDerived中的實現。

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace review 8 { 9 class MyBaseClass //基類 10 { 11 virtual public void Print() 12 { 13 Console.WriteLine("This is the base class."); 14 } 15 } 16 class MyDerivedClass : MyBaseClass //派生類 17 { 18 override public void Print() 19 { 20 Console.WriteLine("This is the derived class."); 21 } 22 } 23 class SecondDerived : MyDerivedClass //最高派生類 24 { 25 public override void Print() 26 { 27 Console.WriteLine("This is the second derived class."); 28 } 29 } 30 class Program 31 { 32 static void Main(string[] args) 33 { 34 SecondDerived derived = new SecondDerived(); 35 MyBaseClass mybc = (MyBaseClass)derived; //強制轉換成基類 36 37 derived.Print(); 38 mybc.Print(); 39 Console.Read(); 40 } 41 } 42 } 43 /* 44 * 輸出如下: 45 * This is the second derived class. 46 * This is the second derived class. 47 * */
結果是:無論Print是通過派生類調用還是通過基類調用,都會調用最高派生類中的方法。當通過基類調用時,調用被沿着繼承層次向上傳遞。
2.情況2:使用new聲明Print
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace review 8 { 9 class MyBaseClass //基類 10 { 11 virtual public void Print() 12 { 13 Console.WriteLine("This is the base class."); 14 } 15 } 16 class MyDerivedClass : MyBaseClass //派生類 17 { 18 override public void Print() 19 { 20 Console.WriteLine("This is the derived class."); 21 } 22 } 23 class SecondDerived : MyDerivedClass //最高派生類 24 { 25 new public void Print() 26 { 27 Console.WriteLine("This is the second derived class."); 28 } 29 } 30 class Program 31 { 32 static void Main(string[] args) 33 { 34 SecondDerived derived = new SecondDerived(); 35 MyBaseClass mybc = (MyBaseClass)derived; //強制轉換成基類 36 37 derived.Print(); 38 mybc.Print(); 39 Console.Read(); 40 } 41 } 42 } 43 /* 44 * 輸出如下: 45 * This is the second derived class. 46 * This is the derived class. 47 * */
結果是:當方法Print通過SecondDerived的引用調用時,SecondDerived中的方法被執行,正如所期待的那樣。然而,當方法通過MyBaseClass的引用調用時,方法調用只向上傳遞了一級,到達類MyDerived,在那里它被執行。

本來想使用下列代碼進行 virtual -> override -> new ->override 的檢驗
1 class ThirdDerived : SecondDerived 2 { 3 override public void Print() 4 { 5 Console.WriteLine("This is the third derived class."); 6 } 7 }
然而編譯器報錯為“ThirdDerived.Print()”: 繼承成員“SecondDerived.Print()”未標記為 virtual、abstract 或 override,無法進行重寫。
不過,當試驗virtual-> override->new ->virtual ->override 時卻是可以的,而實際上,這個中間多出的virtual是在對之前的一系列方法進行隱藏,即這個virtual其實應該寫作 new virtual。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace review 8 { 9 class MyBaseClass //基類 10 { 11 virtual public void Print() 12 { 13 Console.WriteLine("This is the base class."); 14 } 15 } 16 class MyDerivedClass : MyBaseClass //派生類 17 { 18 override public void Print() 19 { 20 Console.WriteLine("This is the derived class."); 21 } 22 } 23 class SecondDerived : MyDerivedClass //最高派生類 24 { 25 new public void Print() 26 { 27 Console.WriteLine("This is the second derived class."); 28 } 29 } 30 class ThirdDerived : SecondDerived 31 { 32 new virtual public void Print() 33 { 34 } 35 } 36 class FourthDerived : ThirdDerived 37 { 38 override public void Print() 39 { 40 Console.WriteLine("This is the fourth derived class."); 41 } 42 } 43 class Program 44 { 45 static void Main(string[] args) 46 { 47 FourthDerived derived = new FourthDerived(); 48 MyBaseClass mybc = (MyBaseClass)derived; //強制轉換成基類 49 50 derived.Print(); 51 mybc.Print(); 52 Console.Read(); 53 } 54 } 55 } 56 /* 57 * 輸出如下: 58 * This is the fourth derived class. 59 * This is the derived class. 60 * */
綜上,對覆寫方法的總結為:
通過某個引用對方法進行調用時,實際執行的是從這里出發,連續的一段override覆寫中的最后一個的方法。(中間不可以用virtual隔開)。
Part3 覆蓋其他成員類型
以上已經描述了如何在方法上使用virtual/override,事實上,在屬性事件以及索引器上也是一樣的。e.g.下面的代碼演示了名為MyProperty的只讀屬性,其中使用了virtual/override
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace review 8 { 9 class MyBaseClass //基類 10 { 11 private int _mInt = 5; 12 virtual public int MyProperty 13 { 14 get { return _mInt; } 15 } 16 } 17 class MyDerivedClass : MyBaseClass //派生類 18 { 19 private int _mInt = 10; 20 public override int MyProperty 21 { 22 get 23 { 24 return _mInt; 25 } 26 } 27 } 28 class Program 29 { 30 static void Main(string[] args) 31 { 32 MyDerivedClass derived = new MyDerivedClass(); 33 MyBaseClass mybc = (MyBaseClass)derived; //強制轉換成基類 34 35 Console.WriteLine(derived.MyProperty); 36 Console.WriteLine(mybc.MyProperty); 37 Console.Read(); 38 } 39 } 40 } 41 /* 42 * 輸出如下: 43 * 10 44 * 10 45 * */
這里需要注意的是,最終執行下去返回的屬性就是派生類的屬性。
p.s. 使用virtual聲明,並不影響方法or屬性or事件or索引的執行(字段無法被virtual修飾)。virtual修飾的方法or屬性or事件or索引仍可以有函數體。
如下例所示
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace review 8 { 9 class MyBaseClass //基類 10 { 11 private int _mInt = 5; 12 virtual public int MyProperty 13 { 14 get { return _mInt; } 15 } 16 } 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 MyBaseClass mybc = new MyBaseClass(); 22 Console.WriteLine(mybc.MyProperty); 23 Console.Read(); 24 } 25 } 26 } 27 /* 28 * 輸出如下: 29 * 5 30 * */
