从元数据角度看抽象方法,接口方法和虚方法的区别。

  1,virtual方法

        代码如下:

  public class VirtualClass
    {
        public virtual void virtualMethod()
        {
            Console.WriteLine("in virtual");
        }

        public virtual void virtualMethod2() {
            Console.WriteLine("sssssssss");
        }
        public void commonMethod() { }
    
    }

    public class DerivedClass : VirtualClass
    {
        public override void virtualMethod()
        {
            Console.WriteLine("sss");
        }

        public new void virtualMethod2()
        {
            base.virtualMethod2();
        }
    }

使用ILDASM查看一下编译后的程序集的元数据,virtualClass类中的三个方法的元数据如下所示:

Method #1 (0600000d)
    -------------------------------------------------------
        MethodName: virtualMethod (0600000D)
        Flags     : [Public] [Virtual] [HideBySig] [NewSlot]  (000001c6)
        RVA       : 0x000020a0
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.

    Method #2 (0600000e)
    -------------------------------------------------------
        MethodName: virtualMethod2 (0600000E)
        Flags     : [Public] [Virtual] [HideBySig] [NewSlot]  (000001c6)
        RVA       : 0x000020ae
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.

    Method #3 (0600000f)
    -------------------------------------------------------
        MethodName: commonMethod (0600000F)
        Flags     : [Public] [HideBySig] [ReuseSlot]  (00000086)
        RVA       : 0x000020bc
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.

第一个方法virtualMethod的flags标记是重点,内容如下:

      Flags     : [Public] [Virtual] [HideBySig] [NewSlot]  (000001c6)

可以发现,每当定义一个virtual方法,编译器就会在这个方法的元数据flag字段中,设置virtual,newslot标记。这里面newslot标记是重中之重,编译器会在该类的虚方法中新建一个一个槽,这样派生类就可以override他了。

        做为对比,可以看一下commonMethod方法,可以发现他的flags里面既没有virtual,也没有newslot,这说明他不能享受viruta方法所带来的种种特殊待遇。

       

       

2.abstract方法

   代码如下:

    public abstract class AbstractClass
    {
        public abstract void abstractMethod1();
        public abstract void abstractMethod2();
    }

    public class NonAbstract : AbstractClass
    {
        public override void abstractMethod1()
        {
            int a;
        }

        public override void abstractMethod2()
        {
            throw new NotImplementedException();
        }

     
    }

abstract中方法的元数据表如下所示:

Method #1 (06000005)
    -------------------------------------------------------
        MethodName: abstractMethod1 (06000005)
        Flags     : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract]  (000005c6)
        RVA       : 0x00000000
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.

    Method #2 (06000006)
    -------------------------------------------------------
        MethodName: abstractMethod2 (06000006)
        Flags     : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract]  (000005c6)
        RVA       : 0x00000000
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.


 与virtual方法不同的是,abstract方法的flags中又多了一个abstract标记,这个标记就告诉编译器,这个方法不应该有具体实现,这一点可以从RVA字段中可以看出,任何abstract方法的RVA都是0。这里可以发现,abstract方法除了多了一个abstract标记以外,与virtual方法没有任何区别。


3.接口方法

 代码如下:

 public interface interface1
    {
        void interface1method();
    }
 public class interfaceClass : interface1//, interface2
    {
        public void interface1method()
        {
            int a;
        }
    }

   interface中方法的元数据如下:

Method #1 (06000001)
    -------------------------------------------------------
        MethodName: interface1method (06000001)
        Flags     : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract]  (000005c6)
        RVA       : 0x00000000
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.

从这里可以看出,接口方法和抽象方法没有什么却别,都是virtual,abstract,newslog这三个标记。他们之间最主要的却别在于派生类的使显示上,下面来看一下interfaceClass类中方法的元数据,这个类实现了interface1接口。

    Method #1 (06000003)
    -------------------------------------------------------
        MethodName: collectionThinking.interface1.interface1method (06000003)
        Flags     : [puglic] [Final] [Virtual] [HideBySig] [NewSlot]  (000001e1)
        RVA       : 0x00002050
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        hasThis
        ReturnType: Void
        No arguments.

这里我们发现,flags中多了一个final标记,这个标记告诉编译器,这个方法不能被override,也就是说我们不能写出如下的方法来:

    public class secondLevalClass : interfaceClass
    {
        public override void   interface1Method()
        {}
    }

如果写出这样的代码来,编译器会给出一个错误,提示没有找到合适的方法来重写interfaceMethod方法。这是因为interfaceClass中的interface1Method方法具有final标记。


然而实现一个抽象方法则不具有这个中限制,实现一个抽闲方法时,编译器不会加上final标记,所以派生类可以override基类中的方法。


总结:

不管是虚方法,接口方法还是抽象方法,都具有一个共同的特点:virtual和newslog标记。其中newslot尤其重要,有了这个标记,派生类就可以override了。

对于接口方法和抽象方法,有一个abstract标记,这个标记告诉编译器,这个方法不能有任何实现,并且类不能被实例化。

从一个接口中继承方法时,如果不显示指定virtual关键字,编译器会在最终生成的元数据中加上一个final标记,这样派生类就不能实现了他了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值