前言:
現在正在讀《你必須知道的.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 }
代碼看似很簡單,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 }
根據以上代碼,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 }
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》一書中講到的那一段話,最初看那一段話的時候感覺迷迷糊糊,但是結合着這個例子再來理解,就好多了。
如果本文有錯誤,后者表述不當,還請多多賜教、留言!