本篇博客我們來學習面向對象的多態性,多態性主要的表現形式就是在繼承中當派生類從基類繼承時,它會獲得基類的所有方法、字段、屬性和事件。當父類和子類的成員簽名相同的時候,我們可以定義讓子類的這個同名成員以何種形式顯示出來,父類的這個的成員在子類中又用何種方式存在,這種多面的表現方法我們稱爲多態。如果我們要更改基類的數據和功能時,也就是說子類中可以以自己的方式去實現父類的功能,有兩種方式:1.我們還在子類中可以使用override關鍵字重寫基類中用virtual關鍵字修飾的虛擬的基成員;2.可以在子類中使用new關鍵字讓派生成員替換基成員,此時基類成員只是被有意的隱藏掉了。
舉一個現實生活的例子,我們來理解一下多態,爸爸如果有一套房子留給了兒子,並對兒子說:這個房子我裝修了一下,如果你不喜歡這個風格,可以自己再重新翻新一下,但是我就有一個要求,就是這個房子唯一不變就是我喜歡的掛在走廊牆上的一張油畫,你不許拆掉它。兒子欣然同意,拿到鑰匙去看了看房子,兒子有點對爸爸的審美產生了質疑,裝修的風格也太古樸了,於是兒子決定把客廳和臥室全部裝修了,改成了後現代主義的風格,這時發生了我們所說的多態的第一種方式,被稱爲重寫或覆寫,爸爸給房子時,把允許重新裝修的地方用virtual關鍵字修飾,兒子就可以用override關鍵字修飾那些允許重新裝修的房間,這時父親來到這個房子時會發現房子的客廳和臥室全部改變了。裝修完客廳和臥室後,兒子怎麼看都覺得走廊上的油畫太格格不入了,因爲油畫的內容是田園風情,這時兒子想起爸爸不讓動這幅畫,所以兒子想到了一個方法,就買了一副新的抽象派的油畫蓋在爸爸的畫上面,這樣爸爸如果來到家,想看自己的畫,也是存在的,這時發生的就是第二種方式,如果爸爸想在兒子的房子裏看自己的東西,就使用base關鍵字,兒子的新油畫用new關鍵字隱藏住爸爸的話。
通過這個例子我們應該就能理解一點多態的含義了,我們先來學習第一種情況:重寫。在使用重寫的時候,一定要注意到一點,就是爲了讓子類可以完全的重寫父類中的成員,父類在定義這些成員的時候,一定要使用virtual關鍵字,讓可以重寫的方法成爲虛方法,讓可以重寫的屬性成爲虛屬性,子類在重寫這個方法或屬性的時候,將virtual替換成override關鍵字,代表已將父類的成員替換爲了它自己的成員並實現。我們來看一下具體的語法,爲了舉例方便,我採用的是方法重寫的實例,如:
class FatherClass
{public virtual void Method(){}}
class SonClass:FatherClass
{public override void Method(){}}
大家來看看父類中的方法,按照我們上節課所學的如果父類中的成員不是private修飾時,子類中是都可以訪問到一個成員,但是重寫是特殊的,根據我舉的例子大家回憶一下,這時如果父親來到兒子的房子,將會只存在被兒子重新裝修的客廳和臥室,這兩個屋原來的風格都不存在了。也就是說,在子類中,這兩個屋子只有一種形態,就是兒子重新裝修定義的。雖然其他的屋子父親也允許兒子裝修,使用了virtual關鍵字修飾了,但是因爲兒子沒有使用override關鍵字重新裝修,其他幾個屋的表現形態還是父親原先存在的。這就說明了一點,子類要想重寫父類的方法,必須是父類定義了virtual,子類使用override。當如果我想在子類的方法中再用父類的那個被覆蓋的方法時,只需要使用base關鍵字就可以代表繼承的父類了,base關鍵字我們在上節課的類七構造方法的繼承中也學到過。我們把上面的語法在子類的Method方法中,加base用法,基本語法如下:
public override void Method()
{
base.Method();
}
這樣的話,在調用子類的Method方法時,雖然重寫了這個方法,但是使用base關鍵字又再次引用了父類的Method方法。
當父類定義了virtual,而子類的同名方法沒有使用override重寫,這樣是可以的,這時子類中仍然包括父類的方法,也就是說子類中有兩個同名的方法,但是編譯器在我們創建子類對象的時候,會顯示子類的方法,而不會顯示繼承自父類那個同名的方法。如果你使用VS編譯這樣情況時,它會有一個警告:子類的方法將隱藏父類的方法。如果想重寫請在子類中加入override關鍵字,如果想隱藏請使用new關鍵字。使用override關鍵字剛纔我們已經講過了,如果用new就代表創建一個新的方法,這個子類中的新方法隱藏父類的方法,這就是我們說的第二種多態的形式隱藏父類的方法。
隱藏父類方法,父類可以是virtual修飾的虛方法,也可以是普通方法應該在子類中顯式的使用new關鍵字,告知編譯器當調用子類的這個方法時,請顯示出子類自定義的功能,當然如果要顯示父類的方法也同樣可以使用base關鍵字,我們來看下面的語法:
class FatherClass
{public void Method(){}}
class SonClass:FatherClass
{public new void Method()
{
base.Method();
}
}
我今天所舉出的實例比較簡單,就是定義了3個類一個包含入口函數的Program類、一個F類,一個S類繼承了F類,在F和S中各定義了3個方法,對照結果我們來看一下:
重寫和隱藏父類方法實例
1 //定義一個F類
2 class F
3 {
4 //定義一個公有Method方法,子類中用new隱藏了本方法
5 public void Method()
6 {
7 Console.WriteLine("我是父類中的Method方法");
8 Console.WriteLine();
9 }
10 //定義了一個虛方法Method1,子類中重寫了Method1.
11 public virtual void Method1()
12 {
13 Console.WriteLine("我是父類中的Method1方法");
14 Console.WriteLine();
15 }
16 //定義了一個虛方法Method2,子類中重寫了Method2.子類中使用了base.method1,再次調用被重寫了的Method1
17 public virtual void Method2()
18 {
19 Console.WriteLine("我是父類中的Method2方法");
20 Console.WriteLine();
21 }
22 }
23 //定義一個繼承F類的S類
24 class S : F
25 {
26 //定義一個公有Method方法,隱藏了父類的方法
27 public new void Method()
28 {
29 Console.WriteLine("我是子類中的Method方法,使用new關鍵字,顯式的隱藏父類中的同名方法");
30 Console.WriteLine();
31 }
32 //定義一個重寫父類Method1的方法Method1。
33 public override void Method1()
34 {
35 Console.WriteLine("我是子類中的Method1方法,覆蓋父類中的同名方法,此時S中沒有了父類中Method1方法");
36 Console.WriteLine();
37 }
38 //定義一個重寫父類Method1的方法Method1,同時使用base調用父類的Method1。
39 public override void Method2()
40 {
41
42 Console.WriteLine("我是子類中的Method2,覆蓋父類中的同名方法,調用base.method1");
43 base.Method1();
44 Console.WriteLine();
45 }
46 }
47
48
49 class Program
50 {
51 static void Main(string[] args)
52 {
53 // 創建一個S類的s對象
54 S s = new S();
55 // 創建一個F類的f對象
56 F f = new F();
57
58 //創建一個F類的fs對象,但是用S類來實現,這種方式是允許的。
59 //意思就是父親到了兒子的房子去了,看到的油畫應該是父親的油畫,房子還是從父親那繼承下的房子。
60 //對象fs所引用出的成員,實際是從父類中繼承來的成員,不是繼承來的成員(如兒子自己買的電器)fs是無權訪問的。
61 //父類需要子類去實現的這種方式會在抽象類和接口中會用到。
62 F fs = new S();
63
64 //先觀察一下子類使用new的Method方法中三對象的結果。
65 //-------------------------------------------
66 //子類的對象s引用的Method方法打印出的是子類的Method方法
67 s.Method();
68 //子類中的Method方法,因爲使用了new,只是覆蓋了父類的Method方法,
69 //所以父類的對象fs引用的Method方法打印出的是還是從父類中繼承下來的Method方法
70 fs.Method();
71 //父類中的Method方法不變
72 f.Method();
73 Console.WriteLine("---------");
74
75
76 //先觀察一下子類使用override的Method1方法中三對象的結果。
77 //-------------------------------------------
78 //子類的對象s引用的Method1方法打印出的是子類的Method1方法
79 s.Method1();
80 //子類中的Method1方法,因爲使用了override,重寫了父類的Method1方法,
81 //所以父類的對象fs引用的Method1方法打印出的是還是從子類中繼承下來的Method1方法
82 //子類中只存在子類的Method1方法,結果如上.
83 fs.Method1();
84 //父類中的Method方法不變
85 f.Method1();
86 Console.WriteLine("---------");
87
88
89 //先觀察一下子類使用override和base.method1的Method2方法中三對象的結果。
90 //-------------------------------------------
91 //因爲使用了override,重寫了父類的Method2方法,
92 //因爲使用了base.Method1,所以在子類中會調用父類的Method1方法
93 //子類的對象s引用的Method1方法打印出的是子類的Method2方法和父類的Method1方法
94 s.Method2();
95 //子類中的Method2方法,因爲使用了override,重寫了父類的Method2方法,
96 //所以父類的對象fs引用的Method2方法打印出的是還是從子類中繼承下來的Method2方法
97 //子類中只存在子類的Method2方法,結果如上.
98 fs.Method2();
99 //父類中的Method方法不變
100 f.Method2();
101 Console.WriteLine("---------");
102 }
103 }
結果如下:
我是子類中的Method方法,使用new關鍵字,顯式的隱藏父類中的同名方法
我是父類中的Method方法
我是父類中的Method方法
---------
我是子類中的Method1方法,覆蓋父類中的同名方法,此時S中沒有了父類中Method1方法
我是子類中的Method1方法,覆蓋父類中的同名方法,此時S中沒有了父類中Method1方法
我是父類中的Method1方法
---------
我是子類中的Method2,覆蓋父類中的同名方法,調用base.method1
我是父類中的Method1方法
我是子類中的Method2,覆蓋父類中的同名方法,調用base.method1
我是父類中的Method1方法
我是父類中的Method2方法
---------
請按任意鍵繼續. . .
在Method2方法中我寫base.Method1(),就想告訴大家,base關鍵字可以調用出父類所能繼承到子類的任何一個成員,同學們課下也可以試一試下面兩種情況:一,如果父類使用virtual,子類使用new,建立第4種Method4方法,也同樣執行這三種對象調用Method4,結果應該和Method方法一致的。還是印證了我說過的當子類中用new時,無論父類是虛方法還是普通方法,都會隱藏父類方法。二,如果同學在子類中只出現override關鍵字,而父類中沒有virtual相呼應,編譯器是會報錯的。
相信通過這個實例大家應該會應用這四個關鍵字了,可以有的同學還是不明白父類用子類來實現的方式,沒關係!用我說的爸爸到兒子家的例子仔細想想就能明白了,這種用法最常見的形式就是當抽象類和接口要實例成對象的時候,都是採用子類來實現的,我們下節課會講到。