前言:
现在正在读《你必须知道的.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》一书中讲到的那一段话,最初看那一段话的时候感觉迷迷糊糊,但是结合着这个例子再来理解,就好多了。
如果本文有错误,后者表述不当,还请多多赐教、留言!