一直以來都對面向對象的語言如何實現虛方法和重寫的這些問題很感興趣,雖然知道最基本的一些如何使用這種設計方式,但是卻一直沒有找到一個詳細的資料告訴我C#到底是如何做到的。
最近沒什麼事情就把一直想看的<<essential .net>>這本書翻了一下,讓我對這件事有了一定程度了解(至少我認為是這樣)。
因為這個問題會涉及到方法表,所以我認為應該先把對象如何在內存中存儲的來做一個簡單的介紹。下面就是一張說明對象的存儲的圖(完全是從書上來的),
假設我們有這樣一個聲明語句: Human b = new Human();
那麼object reference就是我們聲明的變量b, 他就是一個指針, 他指向的位置就是一個對象真正被存儲的地方Object(Human)。在這塊區域上主要有三個部分, 第一個部分是一個對這個對象的索引,具體的功能我還不清楚,但是他在這裡沒有用到,所以可以忽略。第二部分htype是一個指針, 他指向的位置存儲的是這個實例所屬的類的信息。 第三部分是存儲這個類的數據成員。
在這裡重點是關於第二個指針htype的。htype指向的區域被作者稱為CORINFO_CLASS_STRUCT, 對於每一個類都有這樣一個區域,而且應該是唯一的。在CORINFO_CLASS_STRUCT這塊區域中,只要知道有一下三個區域就可以了:
有了前兩項內容就建立起來類與類之間的關系了,對象的類型轉換就是通過這個來判斷的,但我們關注的主要還是方法表。
為了配合問題的說明,我定義了兩個簡單的類Base和Child。Base 中的方法大部分都是虛方法, 然後在Child中也定義相同的方法,但是前面的修飾符不同。
1 public class A
2 {
3 static void Main()
4 {
5 Base b = new Child();
6 Child ch = new Child();
7
8 b.print();
9 b.print2();
10 b.print3();
11 b.print4();
12
13 ch.print();
14 ch.print2();
15 ch.print3();
16 ch.print4();
17
18 Console.ReadKey();
19 }
20 }
21
22 public class Base
23 {
24 public int x = 8;
25
26 public Base() { }
27
28
29 public void print()
30 {
31 Writer.OutPut("a");
32 }
33
34 public virtual void print2()
35 {
36 Writer.OutPut("b");
37 }
38
39 public virtual void print3()
40 {
41 Writer.OutPut("c");
42 }
43
44 public virtual void print4()
45 {
46 Writer.OutPut("d");
47 }
48
49 }
50
51 public class Child : Base
52 {
53 public Child() { }
54 public void print()
55 {
56 Writer.OutPut("e");
57 }
58
59 public override void print2()
60 {
61 Writer.OutPut("f");
62 }
63 public new virtual void print3()
64 {
65 Writer.OutPut("g");
66 }
67 public virtual void print4()
68 {
69 Writer.OutPut("h");
70 }
71 }
這段程序在被編譯成IL後, 我們可以看一下main方法是怎麼樣的
1.method private hidebysig static void Main() cil managed
2{
3 .entrypoint
4 .maxstack 1
5 .locals init (
6 [0] class FormatNumber.Base b,
7 [1] class FormatNumber.Child ch)
8 L_0000: nop
9 L_0001: newobj instance void FormatNumber.Child::.ctor()
10 L_0006: stloc.0
11 L_0007: newobj instance void FormatNumber.Child::.ctor()
12 L_000c: stloc.1