天天看點

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

目錄:

<a href="http://yisuowushinian.blog.51cto.com/4241271/1352834" target="_blank">【C#小知識】C#中一些易混淆概念總結--------資料類型存儲位置,方法調用,out和ref參數的使用</a>

<a href="http://www.cnblogs.com/qq731109249/p/3525271.html" target="_blank"></a>

----------------------------------分割線--------------------------------------

這一系列的文章在園子裡還是比較受歡迎的。有一些留言指出了其中理論性的錯誤,怎麼寫出來這些文章的,有沒有教育訓練過等等問題。

下面就一并的回答這些問題吧。

1)自己今年六月份畢業,現在在帝都實習。不過在學校已經做過一些C#開發了,現在也是做.NET開發工作。

2)文章中很多知識是自己以前在網上下載下傳的視訊教程,學習過程中所記的筆記。也就是在年前的時候,突然有一天發現自己的筆記本記了差不多塊一本了,之前也沒時間整理過,是以就想着把它們整理成部落格文章,順便溫習一下這些筆記知識。

3)有園友問自己是不是在傳智教育訓練過。首先說我沒有教育訓練過,但是非常感謝傳智公開的一些自學教程。因為自己也是這些視訊的受益者,學到了很多知識,養成了一些好的學習習慣。

4)在整理筆記的過程中遇到了很多問題,其中自己參考了《C#本質論》,《CLR via C#》還有就是MSDN的官方文檔。

3)不管怎樣還是會遇到一些自己解決不掉或者弄不清楚的問題,這個過程使用了Google搜尋并和請教了一些園友。

4)錯誤總是會存在。謝謝看我部落格的讀者你們的細心,指出了我博文中的錯誤。确定這些錯誤後,我都立即修改了自己的文章。

---------------------------------------------分割線-----------------------------------------------------

今天開始上班了。這幾天研究學習了一下思維導圖,感覺用它整理自己的知識非常的友善。是以,以後寫部落格完成一個知識點,都會用思維導圖做一個總結。也能讓大家對所要讀的内容有一個整體的把握。

我用的思維導圖軟體是FreeMind(免費的,但是得裝JDK),因為剛開始學習使用,很多操作技巧不是很熟練,做出來的導圖估計也不是很好,希望大家見諒。

首先,裡氏替換原則。

這是了解多态所必須掌握的内容。對于裡氏替換原則維基百科給出的定義如下:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

為什麼子類可以替換父類的位置,而程式的功能不受影響呢?

當滿足繼承的時候,父類肯定存在非私有成員,子類肯定是得到了父類的這些非私有成員(假設,父類的的成員全部是私有的,那麼子類沒辦法從父類繼承任何成員,也就不存在繼承的概念了)。既然子類繼承了父類的這些非私有成員,那麼父類對象也就可以在子類對象中調用這些非私有成員。是以,子類對象可以替換父類對象的位置。

來看下面的一段代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

<code>class</code> <code>Program</code>

<code>    </code><code>{</code>

<code>        </code><code>static</code> <code>void</code> <code>Main(</code><code>string</code><code>[] args)</code>

<code>        </code><code>{</code>

<code>            </code><code>Person p = </code><code>new</code> <code>Person();</code>

<code>            </code><code>Person p1 = </code><code>new</code> <code>Student();</code>

<code>            </code><code>Console.ReadKey();</code>

<code>        </code><code>}</code>

<code>    </code><code>}</code>

<code>    </code><code>class</code> <code>Person</code>

<code>    </code><code>//父類的私有成員</code>

<code>    </code><code>private</code> <code>int</code> <code>nAge;</code>

<code>        </code><code>public</code> <code>Person()</code>

<code>            </code><code>Console.WriteLine(</code><code>"我是Person構造函數,我是一個人!"</code><code>);</code>

<code>        </code><code>public</code> <code>void</code> <code>Say()</code>

<code>            </code><code>Console.WriteLine(</code><code>"我是一個人!"</code><code>);</code>

<code>    </code><code>class</code> <code>Student : Person</code>

<code>        </code><code>public</code> <code>Student()</code>

<code>            </code><code>Console.WriteLine(</code><code>"我是Student構造函數,我是一個學生!"</code><code>);</code>

<code>        </code><code>public</code> <code>void</code> <code>SayStude()</code>

<code>            </code><code>Console.WriteLine(</code><code>"我是一個學生!"</code><code>);</code>

<code>    </code><code>class</code> <code>SeniorStudent : Student</code>

<code>        </code><code>public</code> <code>SeniorStudent()</code>

<code>            </code><code>Console.WriteLine(</code><code>"我是SeniorStudent構造函數,我是一個高中生!"</code><code>);</code>

<code>        </code><code>public</code>  <code>void</code> <code>SaySenior()</code>

<code>            </code><code>Console.WriteLine(</code><code>"我是一個高中生!"</code><code>);</code>

我們運作列印出的結果是:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

根據前面的構造函數的知識很容易解釋這個結果。那麼我們在Main()函數中添加如下的代碼:

<code>static</code> <code>void</code> <code>Main(</code><code>string</code><code>[] args)</code>

<code>            </code><code>p.Say();</code>

<code>  </code>

<code>            </code><code>p1.Say();</code>

在通路的過程中,可以發現p隻可以通路父類的say

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

而p1也隻可以通路父類的Say方法

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

其實在上面的代碼中,就滿足了裡氏替換原則。子類的Student對象,替換了父類Person對象的位置。

那麼它們在記憶體中發生了些什麼呢?如下圖:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

由上可以知道,當一個父類的變量指向一個子類對象的時候隻能通過這個父類變量調用父類成員,子類獨有的成員無法調用。

同理我們可以推理出,子類的變量是不可以指向一個父類的對像的

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

但是當父類變量指向一個子類變量的時候,可以不可以把父類的變量轉化成子類的對象呢?看下圖

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

關于引用類型的兩種轉換方式:

由上面的代碼我們已經知道了一種轉換,就是在變量錢直接加需要轉換的類型,如下代碼:

那麼第二種轉換方式就是使用as關鍵字,如下代碼:

使用as關鍵字和第一種強制轉換的差別就是,第一種如果轉換失敗會抛異常,第二種轉換失敗則傳回一個null值。

思維導圖總結如下:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

二,虛方法

使用virtual關鍵字修飾的方法,叫做虛方法(一般都是在父類中)。

看下面的一段代碼:

<code>class</code> <code>Person</code>

<code>        </code><code>private</code> <code>int</code> <code>nAge;</code>

<code>        </code><code>//這裡定義了一個虛方法</code>

<code>        </code><code>public</code> <code>virtual</code> <code>void</code> <code>Say()</code>

<code>        </code><code>//子類使用override關鍵字改寫了父類的虛方法</code>

<code>        </code><code>public</code> <code>override</code> <code>void</code> <code>Say()</code>

緊接着在main()函數中添加如下的代碼:

<code>            </code><code>Student s = </code><code>new</code> <code>Student();</code>

<code>            </code><code>s.Say();</code>

列印結果如下:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

我們很明顯的可以發現,第二個表達式滿足裡氏替換原則,p1.Say()執行的應該是父類的Say()方法,但是這裡卻執行了子類的Say()方法。

這就是子類使用override關鍵字的Say()方法覆寫了父類的用Virtual關鍵字修飾的Say()方法。

我們使用動态圖檔看一下調試過程,

①首先是沒有使用任何關鍵字:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

由上可以看出直接跳入父類,執行了父類的Say()方法;

②再看使用virtual和override關鍵字的動态調試圖檔,如下:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

可以看到直接到子類去執行override關鍵字修飾的Say()方法。

那麼如果父類使用virtual關鍵字修飾,而子類沒有重寫該方法時會怎麼樣呢?如下面的代碼:

<code>               </code> 

<code>        </code><code>//子類中沒有出現override關鍵字修飾的方法</code>

執行結果如下:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

是以,如果子類找不到override方法,則會回溯到該子類的父類去找是否有override方法,知道回溯到自身的虛方法,并執行。

虛方法知識總結的思維導圖如下:

【C#小知識】C#中一些易混淆概念總結(六)---------解析裡氏替換原則,虛方法

     本文轉自yisuowushinian 51CTO部落格,原文連結:http://blog.51cto.com/yisuowushinian/1357174,如需轉載請自行聯系原作者