zoukankan      html  css  js  c++  java
  • 【Emit基础】调用Tostring()方法的IL表示

          首先看一个例子: 

            public void Test(int a, StudentType studentType)
            {
                
    string ss2 =
     a.ToString();
                
    string ss =
     studentType.ToString();
            }

          StudentType 是一个枚举类型,那么Test方法对应的IL是什么样子了?int和枚举的ToString方法的IL表示是一样的吗?我们来揭开谜底:

        .maxstack 1
        .locals init (
            [
    0string ss2,
            [
    1string
     ss)
        L_0000: nop 
        L_0001: ldarga.s a
        L_0003: call instance 
    string
     [mscorlib]System.Int32::ToString()
        L_0008: stloc.
    0
     
        L_0009: ldarg.2
     
        L_000a: box TheTest.StudentType
        L_000f: callvirt instance 
    string
     [mscorlib]System.Object::ToString()
        L_0014: stloc.
    1
     
        L_0015: ret 

        虽然,StudentType和Int都是值类型,但是Tostring()方法的调用却是如此的不同,我们来逐一分析。

    1.方法调用(call、callvir)只能基于引用来调用方法。

         我们如果想调用int的Tostring()方法,就必须先获取其“引用”,但是,值类型不存在引用,所以我们使用ldarga.s来获取其地址(指针就相当于引用了)。

         而对于枚举类型,先将其Box获取装箱对象的引用,然后再调用Tostring()方法。

         注意,IL虚拟机的针对内置类型的基本操作(如 add),直接基于对象本身操作即可,而不是针对引用(对象的地址)进行。

    2.对于.NET的内置基础值类型,使用call直接调用其自身的Tostring()方法。对于枚举这样的值类型,则需要使用callvirt来调用object的Tostring()虚方法。

     

         我们再来看个带ref参数的例子:

            public void Test2(ref int a, ref StudentType studentType)
            {
                
    string ss2 =
     a.ToString();
                
    string ss =
     studentType.ToString();
            }

         其对应的IL如下:

        .maxstack 1
        .locals init (
            [
    0string ss2,
            [
    1string
     ss)
        L_0000: nop 
        L_0001: ldarg.1
     
        L_0002: call instance 
    string
     [mscorlib]System.Int32::ToString()
        L_0007: stloc.
    0
     
        L_0008: ldarg.
    2
     
        L_0009: ldind.i4 
        L_000a: box TheTest.StudentType
        L_000f: callvirt instance 
    string
     [mscorlib]System.Object::ToString()
        L_0014: stloc.
    1
     
        L_0015: ret 

         说明如下:

    3.由于ref传递的本来就是地址,所以针对第一个参数,可以直接加载参数然后调用Tostring()方法。

    4.由于StudentType不是IL内置的基础类型,IL不认识它,所以必须先通过ldind.i4间接加载对象,接着装箱后再调用Tostring()方法。注意:默认的枚举是用int来表示的,这也是使用ldind.i4而不是ldind.i2的原因。 

     

          最后,再看一个引用类型的例子:

            public void Test3(ref Student a)
            {
                
    string ss2 =
     a.ToString();          
            }

         对应的IL如下:

        .maxstack 1
        .locals init (
            [
    0string ss2)
        L_0000: nop 
        L_0001: ldarg.
    1
     
        L_0002: ldind.ref 

        L_0003: callvirt instance 
    string [mscorlib]System.Object::ToString()
        L_0008: stloc.
    0
     
        L_0009: ret 

         有了上面的基础,这段代码很容易理解,由于参数传入的是对象引用的地址(即,地址的地址),所以先要通过ldind.ref把对象的引用加载到计算堆栈上,然后才能调用方法。注意,自定义class的ToString()方法都是使用callvirt进行调用的。

     

     

     

  • 相关阅读:
    一个简单的批量更新oracle 数据库中 最近的服务商名称的数据
    sql 分组后显示每组的前几条记录
    基于server broker 的数据实时更新
    Oracle中的三种Join 方式
    weblogic内存调整说明
    TongWeb
    国产数据库助力民航核心交易系统
    weblogic的集群与配置
    项目管理软考
    浮躁的过去,开启的项目管理之路(四)
  • 原文地址:https://www.cnblogs.com/zhuweisky/p/1294666.html
Copyright © 2011-2022 走看看