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