zoukankan      html  css  js  c++  java
  • 解惑《你必须知道的.net》——C#继承关系中【方发表】的创建和调用

    前言:

    现在正在读《你必须知道的.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》一书中讲到的那一段话,最初看那一段话的时候感觉迷迷糊糊,但是结合着这个例子再来理解,就好多了。

    如果本文有错误,后者表述不当,还请多多赐教、留言!

  • 相关阅读:
    从Linux内核中获取真随机数【转】
    linux下pthread_cancel无法取消线程的原因【转】
    LINUX-内核-中断分析-中断向量表(3)-arm【转】
    ARM中断向量表与响应流程【转】
    小任务与工作队列的区别【转】
    GNU Readline 库及编程简介【转】
    Linux 内核同步之自旋锁与信号量的异同【转】
    Professional Linux Kernel Architecture 笔记 —— 中断处理(Part 2)【转】
    Linux中断(interrupt)子系统之二:arch相关的硬件封装层【转】
    浅谈C语言中的强符号、弱符号、强引用和弱引用【转】
  • 原文地址:https://www.cnblogs.com/wangfupeng1988/p/3629312.html
Copyright © 2011-2022 走看看