深入剖析new override和virtual關鍵字


    在老師上課期間,老師只不過很簡單的介紹了一下new、override、virtual這幾個關鍵字。上課根本就沒有消化,直到自己在看博客園中王濤寫的《你必須知道的.Net》和網上一些資料的后,才弄明白了其中的含義。我想並不是每個人都有機會和心思去讀一本好幾百頁的書的,所以肯定還有很多初學者和像我一樣一開始不懂的人。而我在這里也只不過分享一下自己的體會。如果有什么不對,請高手指出,我將做修改。

Vitual:

     在MSDN上面的解釋為virtual 關鍵字用於修飾方法、屬性、索引器或事件聲明,並使它們可以在派生類中被重寫。例如,此方法可被任何繼承它的類重寫。(MSDN版本為MSDN Library for Visual Studio 2008 簡體中文,如果下面沒有特殊說明就說明參考的地方是一樣的)。

New:

     在 C# 中,new 關鍵字可用作運算符、修飾符或約束。

     New在定義的時候有3中用法。

     new 運算符 用於創建對象和調用構造函數。

     new 修飾符 用於向基類成員隱藏繼承成員。

  new 約束 用於在泛型聲明中約束可能用作類型參數的參數的類型。

  而今天我所講的是其中第二種修飾符。

Override:

  要擴展或修改繼承的方法、屬性、索引器或事件的抽象實現或虛實現,必須使用 override 修飾符。

 

  上面只不過是大體的介紹了3個關鍵字的含義。接着我們來看一段代碼讓我們徹底的弄明白這3個關鍵字的用法。

 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace 你必須知道的NET.博客園
7 {
8 class 文章1代碼
9 {
10 public static void Main()
11 {
12 Base num = new Base();
13 num.ShowNumber();
14 Derived intNum = new Derived();
15 intNum.ShowNumber();
16 intNum.ShowInfo();
17
18 Base number = new Derived();
19 number.ShowInfo();
20 number.ShowNumber();
21
22 Console.ReadKey();
23 }
24 }
25
26
27
28 class Base {
29 public static int i = 123;
30
31 public virtual void ShowInfo() {
32 Console.WriteLine("base calss ----");
33 }
34
35 public virtual void ShowNumber(){
36 Console.WriteLine(i.ToString());
37 }
38 }
39
40 class Derived : Base {
41 new public static int i = 456;
42
43 public new virtual void ShowInfo() {
44 Console.WriteLine("Derived class");
45 }
46
47 public override void ShowNumber()
48 {
49 Console.WriteLine("Base number is {0}",Base.i.ToString());
50 Console.WriteLine("New number is {0}",i.ToString());
51 }
52 }
53 }

  首先我們從內存分配的角度來看這段代碼,我們先從Managed Heap中分析。如果對內存概念不理解的話,可以先看一下內存分配(堆棧和托管堆)的知識,這樣可以更快的理解我所講的內容。

 

  這個是我畫的內存分配圖,畫的有點亂(我不知道博客園那些高手用的是什么畫圖工具,畫的比我好多了,知道的請給我留言),

首先我們從類定義出發,Base這個類為:

 class Base {
public static int i = 123;

public virtual void ShowInfo() {
Console.WriteLine("base calss ----");
}

public virtual void ShowNumber(){
Console.WriteLine(i.ToString());
}
}

  我們定義這個類的時候在方法中都添加了virtual這個關鍵字,為后面的繼承實現多態提供可能。

  重點是看我們定義的第二個類Derived類,定義如下:

 1  class Derived : Base {
2 new public static int i = 456;
3
4 public new virtual void ShowInfo() {
5 Console.WriteLine("Derived class");
6 }
7
8 public override void ShowNumber()
9 {
10 Console.WriteLine("Base number is {0}",Base.i.ToString());
11 Console.WriteLine("New number is {0}",i.ToString());
12 }
13 }
 

  首先我們看ShowInfo()這個方法,方法中的關鍵字為new,根據定義我們基類中也有同樣的方法,那么我們就隱藏了基類中的ShowInfo()這個方法,但事實上基類中的ShowInfo()還是存在的。接着我們又定義了ShowNumber()這個方法,這個方法的關鍵字是override,根據定義我們首先繼承基類中可以被繼承的成員和函數,然后把基類中原來的ShowNumber()函數找到,然后改寫里面的內容。可能有些人還沒有明白,那我舉個例子,如果有個人給了你一張寫着一篇文章的紙,相當於我們這里的基類,然后你拿到這篇文章看,相當於繼承這個概念,你覺得好的地方可以原封不動的保留着,相當於繼承,有些不怎么好的句子你可以用橡皮擦擦掉改成自己的優美的句子,橡皮擦擦掉后我們在也看不到痕跡了,這里相當於我們說的override這個關鍵字;還有一些不怎么好的句子,我們只不過用一些修改符號來改成自己的句子,在原稿中原來的句子還是存在的,這里相當於我們說的new這個關鍵字。

  在主函數中我們可以看到一開始我們首先定義了一個Base類型的變量num,然后再托管堆中分配一個Base的空間(空間的狀況可以參考上面那張圖中指向為1的那塊部分),就這樣一句Base num = new Base()執行好了,然后我們調用num變量的ShowNumber()這個方法,從圖中可以看出,應該是123.緊接着我們在后面的聲明了Derived類型的intNum這個變量,然后指向了Derived()這塊托管堆的地址。這樣又一句Deriverd intNum = new Derived()在內存中分配好了,接着我們要開始調用它們的方法,showNumber()和ShowInfo()這兩個方法。同理可以從圖中看出應該輸出為Base number is 123 New number is 456和Derived class。

  接着是第二個難點了,我們Base number = new Derived();很多新手對上面的句子還是掌握的,但是到了這句,就糊里糊塗的當做死記記住了。現在我來告訴你一種簡單的記法,我們要先從后面的那部分開始,new Derived() 然后加個is 然后再加前面的變量類型,然后成的句子可以寫成這樣Base number = new Derived() is Base我是把is讀為屬於(這個是我自己為了方便記憶,並沒有什么依據,所以這里我只不過作為一個參考),要是屬於Base這個類那么這種定義是合法的,但是如果反過來定義,那就不行了,如:Derived test = new Base()當編譯的時候就會報錯。

  定義的事我已經講得很清楚了,接着我們來看看內存的分配情況吧,我們定義了一個number這個變量,這個變量指向Derived()這個類,但是問題來了,兩個類型不一致呀,聽我慢慢道來,其實前面的變量只不過是一種范圍,告訴內存從Derived這個類開始我要移動多少的位子,要是移不到的位子,就是讀不出來的位子了。好了我們現在來看ShowInfo()這個函數,以為我們在派生類中用的是new這個關鍵字,所有在派生類中還是保留着這個基類的ShowInfo()(你不知道的話在重新看看前面的),而我們在派生類中新添加進去的ShowInfo()這個方法對於number移動不了這么遠的位子,好了我想你應該明白了輸出的語句是什么了,沒錯就是”Base Class ----”,接着看showNumber()這個函數,因為這個函數在基類的ShowNumber()函數上進行了修改,而number這個變量可以知道基類中任何變量和函數的內容,而知道的內容被改了,所有就只能讀到被改得內容了。可能有些人會問,在這中不是改了方法,那應該在內存中寫在后面或者前面的內存不夠寫的問題,其實真正的內存的分配不是我這樣的,類似於下面那樣圖,我這里只不過是為了方便讀者能理解所有才這么畫,如果有誤導,請見諒。

 

  這個也是大概的類的定義,TypeHandle這個是方法的指針指向所對應的方法的地址。

 

  接着我們在來看看IL代碼,然復習一遍今天所寫的代碼吧.

IL代碼:

 1 .method public hidebysig static void  Main() cil managed
2 {
3 .entrypoint
4 // 代碼大小 61 (0x3d)
5 .maxstack 1
6 .locals init ([0] class '你必須知道的NET.博客園'.Base num,
7 [1] class '你必須知道的NET.博客園'.Derived intNum,
8 [2] class '你必須知道的NET.博客園'.Base number)
9 IL_0000: nop
10 IL_0001: newobj instance void '你必須知道的NET.博客園'.Base::.ctor()
11 IL_0006: stloc.0
12 IL_0007: ldloc.0
13 IL_0008: callvirt instance void '你必須知道的NET.博客園'.Base::ShowNumber()
14 IL_000d: nop
15 IL_000e: newobj instance void '你必須知道的NET.博客園'.Derived::.ctor()
16 IL_0013: stloc.1
17 IL_0014: ldloc.1
18 IL_0015: callvirt instance void '你必須知道的NET.博客園'.Base::ShowNumber()
19 IL_001a: nop
20 IL_001b: ldloc.1
21 IL_001c: callvirt instance void '你必須知道的NET.博客園'.Derived::ShowInfo()
22 IL_0021: nop
23 IL_0022: newobj instance void '你必須知道的NET.博客園'.Derived::.ctor()
24 IL_0027: stloc.2
25 IL_0028: ldloc.2
26 IL_0029: callvirt instance void '你必須知道的NET.博客園'.Base::ShowInfo()
27 IL_002e: nop
28 IL_002f: ldloc.2
29 IL_0030: callvirt instance void '你必須知道的NET.博客園'.Base::ShowNumber()
30 IL_0035: nop
31 IL_0036: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
32 IL_003b: pop
33 IL_003c: ret
34 } // end of method '文章1代碼'::Main

  首先我們申明了一個公共的有CLR來自動管理的不可以繼承的Main()方法。然后定義了3個變量,第一個變量調用Base::.ctor()這個構造函數來初始化自己的變量,然手調用Base::ShowNumber()這個方法。接着第二個變量調用了Derived::.ctor()這個構造函數來初始化自己的方法,然手調用Base::ShowNumber()(被重寫過,但是方法是屬於Base這個類)和.Derived::ShowInfo()(方法是派生類的方法,基類同名的方法被派生類所隱藏掉了),最后一個變量調用的是Derived::.ctor()這個構造函數,然后調用Base::ShowInfo()(Base的ShowInfo()方法,因為是隱藏的但是確確實實是存在的而且找的到的)和Base::ShowNumber()(被派生類重新了,內容改變了)。

  最后的運行結果為:

 

  這個是我對3個關鍵字的總結,希望對那些C#初學者,和c#學習迷茫的人能有所幫助。

 

  最后我也希望那些喜歡編程的,喜歡C#的的程序員們和那些業余愛好者,能跟我一起成長。QQ:371002515,歡迎你們加我QQ進行技術的交流,最后的最后我想說的是微軟因為我們而精彩。

 

 


免責聲明!

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



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