review——C# (6)虛方法和覆寫方法


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  * */

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM