解惑《你必須知道的.net》——C#繼承關係中【方發表】的建立和呼叫

王福朋發表於2014-03-27

前言:

現在正在讀《你必須知道的.net》(第二版)一書,看到IL語言那一章,將call、callvirt和calli時候,書中舉了一個例子,是一個三層繼承的例子,我一開始看的時候就有點懵。

程式碼如下:

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Father son = new Son();       
 6             son.DoWork();                 
 7             son.DoVirtualWork();          
 8             son.DoVirtualAll();           
 9 
10             Father aGrandson = new Grandson();  
11             aGrandson.DoWork();                 
12             aGrandson.DoVirtualWork();          
13             aGrandson.DoVirtualAll();           
14 
15             Console.Read();
16         }
17     }
18 
19     /// <summary>
20     /// Father類
21     /// </summary>
22     public class Father
23     {
24         public void DoWork()
25         {
26             Console.WriteLine("Father.DoWork()");
27         }
28 
29         //虛方法
30         public virtual void DoVirtualWork()
31         {
32             Console.WriteLine("Father.DoVirtualWork()");
33         }
34 
35         //虛方法
36         public virtual void DoVirtualAll()
37         {
38             Console.WriteLine("Father.DoVirtualAll()");
39         }
40     }
41 
42     /// <summary>
43     /// Son類
44     /// </summary>
45     public class Son : Father
46     {
47         //new 
48         public new void DoWork()
49         {
50             Console.WriteLine("Son.DoWork()");
51         }
52 
53         //new virtual
54         public new virtual void DoVirtualWork()
55         {
56             base.DoVirtualWork();
57             Console.WriteLine("Son.DoVirtualWork()");
58         }
59 
60         //override
61         public override void DoVirtualAll()
62         {
63             Console.WriteLine("Son.DoVirtualAll()");
64         }
65     }
66 
67     /// <summary>
68     /// Grandson類
69     /// </summary>
70     public class Grandson : Son
71     {
72         public override void DoVirtualWork()
73         {
74             base.DoVirtualWork();
75             Console.WriteLine("Grandson.DoVirtualWork()");
76         }
77 
78         public override void DoVirtualAll()
79         {
80             base.DoVirtualAll();
81             Console.WriteLine("Grandson.DoVirtualAll()");
82         }
83     }
View Code

程式碼看似很簡單,Grandson繼承了Son,Son繼承了Father。main()方法中,宣告瞭兩個例項,呼叫例項方法。但是執行的結果卻讓我懵圈了,結果如下:

 

費盡周折倆小時想不出,我忽然想起書中一開始將繼承的時候,將到了繼承關係中方發表的建立,這才弄清楚了這個關係。

本文就以此例子大體講講在繼承關係中,方發表是如何建立的。

 

建立方發表:

當一個物件初始化時,比如:Father son = new Son();  

系統會找到這個物件的類,將它的例項方法新增到方法表中。但是,如果這個類有父類,則先建立父類的。因為子類可能會override父類的方法。以Father son = new Son(); 為例:

 1 /// <summary>
 2     /// Father類
 3     /// </summary>
 4     public class Father
 5     {
 6         public void DoWork()
 7         {
 8             Console.WriteLine("Father.DoWork()");
 9         }
10 
11         //虛方法
12         public virtual void DoVirtualWork()
13         {
14             Console.WriteLine("Father.DoVirtualWork()");
15         }
16 
17         //虛方法
18         public virtual void DoVirtualAll()
19         {
20             Console.WriteLine("Father.DoVirtualAll()");
21         }
22     }
23 
24     /// <summary>
25     /// Son類
26     /// </summary>
27     public class Son : Father
28     {
29         //new 
30         public new void DoWork()
31         {
32             Console.WriteLine("Son.DoWork()");
33         }
34 
35         //new virtual
36         public new virtual void DoVirtualWork()
37         {
38             base.DoVirtualWork();
39             Console.WriteLine("Son.DoVirtualWork()");
40         }
41 
42         //override
43         public override void DoVirtualAll()
44         {
45             Console.WriteLine("Son.DoVirtualAll()");
46         }
47     }
View Code

根據以上程式碼,new一個Son類的物件的時候,會先找到Son類的父類——Father類,將Father的例項方法放在方法表中,就會有以下方發表結構:

(注:其實最先建立的是Object類的方法表,因為Object是所有類的父類,此處略過不講)

接下來就會在將Son類的方法放入方發表中,此時要注意new和override。new其實相當於給方法重新命名,雖然方法名和父類一樣,但是是屬於子類的另外一個方法了。override會重寫父類方法,可以簡單的理解為重寫方法體。這是方發表就會變為:

所以,Father son = new Son();   最終的方發表會變成這樣。

1             Father son = new Son();       
2             son.DoWork();          
3             son.DoVirtualWork();          
4             son.DoVirtualAll();          

執行以上程式碼時,會從方發表中自上而下查詢,所以才會出現這種結果:

 

好了,繼續跟著程式往下走,看看new Grandson()時,方發表又發生了哪些改變。

 1 /// <summary>
 2     /// Grandson類
 3     /// </summary>
 4     public class Grandson : Son
 5     {
 6         public override void DoVirtualWork()
 7         {
 8             base.DoVirtualWork();
 9             Console.WriteLine("Grandson.DoVirtualWork()");
10         }
11 
12         public override void DoVirtualAll()
13         {
14             base.DoVirtualAll();
15             Console.WriteLine("Grandson.DoVirtualAll()");
16         }
17     }
View Code

Grandson整合了Son類,在執行new Grandson()時,會先把Father的方法寫入方發表,然後再是Son,這兩個的方發表上文都畫出了。最後在建立Grandson類的方發表。

Grandson類有兩個例項方法,都重寫了父類的虛方法。但是這兩個方法有區別:DoVirtualWork()在Son類中已經被new,所以這裡Grandson重寫的DoVirtualWork()是Son類new之後的DoVirtualWork(),和Father類的DoVirtualWork()已經沒有關係。而對DoVirtualAll()的重寫將會修改Father類的DoVirtualAll()方法。

此時方發表將會是這樣:

執行方法:

1 Father aGrandson = new Grandson();  
2 aGrandson.DoWork();              
3 aGrandson.DoVirtualWork();  
4 aGrandson.DoVirtualAll();  

執行時,從方發表自上而下獲取方法,執行,才會出現以下結果:

 

總結:

其實我對方發表的理解,也就是《你必須知道的.net》一書中講到的那一段話,最初看那一段話的時候感覺迷迷糊糊,但是結合著這個例子再來理解,就好多了。

如果本文有錯誤,後者表述不當,還請多多賜教、留言!

 

相關文章