看懂IL代码

//示例一:输出整数的立方值。

private void PrintCube( int i )

{

    int cube = i * i * i;

    Console.WriteLine( cube );

}

//方法签名。

/**//// hidebysig:MethodAttributes 枚举值之一,指示此方法按名称和签名隐藏,否则只

/// 按名称隐藏。

/// cil managed:未查到具体资料,应是“受中间语言管理”之意。



.method private hidebysig instance void 

          PrintCube(int32 i) cil managed

  {

    // 代码大小       15 (0xf)

    .maxstack  2

    /**//**//**//// 在 .locals 部分声明所有的局部变量。

    .locals init ([0] int32 cube)    /**//**//**//// 第一个名局部变量,int 型,名为 cube。索

                                    /// 引从 0 开始。

    IL_0000:  nop    /**//**//**//// no operation.

    IL_0001:  ldarg.1    /**//**//**//// load argument 第一个方法参数入栈,比如“3”。索引号

                        /// 从 1 开始,而不是从 0 开始。

    IL_0002:  ldarg.1    /**//**//**//// 再次向堆栈压入第一个方法参数,又一个“3”。

    IL_0003:  mul    /**//**//**//// multiply 计算堆栈最顶上两个数的乘积 3×3,并把结果入栈,

                    /// 即堆栈最顶部是 9 了。

    IL_0004:  ldarg.1    /**//**//**//// 再次压入第一个方法参数“3”。

    IL_0005:  mul    /**//**//**//// 堆栈最顶上是“3”,第二是“9”,计算 3×9,此时 27 入栈。

    IL_0006:  stloc.0    /**//**//**//// pop value from stack to local variable 堆栈最顶上的

                        /// 值“27”出栈,并被赋给索引位置“0”处的局部变量 cube,

                        /// 即内存中变量 cube 的值为“27”。

    IL_0007:  ldloc.0    /**//**//**//// 局部变量 cube 的值“27”入栈。

    IL_0008:  call       void [mscorlib]System.Console::WriteLine(int32)

                        /**//**//**//// 控制台输出堆栈最顶上的 32 位整数“27”。

    IL_000d:  nop    /**//**//**//// no operation.

    IL_000e:  ret    /**//**//**//// return from method.

  } // end of method Program::PrintCube

//示例二:把字符串拆分成字符,并按顺序每行输出一个字符

public void SeparateString( string source )

{

    if( source == null )

        return;



    int count = source.Length;



    char c;

    for( int i = 0; i < count; i++ )

    {

        c = source[ i ];

        Console.WriteLine( c );

    }

}



.method public hidebysig instance void 

          SeparateString(string source) cil managed

  {

    // 代码大小       55 (0x37)

    .maxstack  2

    .locals init ([0] int32 count,

             [1] char c,

             [2] int32 i,

             [3] bool CS$4$0000)    /**//**//**//// 索引为“3”的这个布尔型局部变量在 C# 代

                                    /// 码中并未显式声明,是编译器编译时添加的,

                                    /// 用于保存执行过程中布尔运算的结果,比如比

                                    /// 较 source 是否为空时,以及比较 i<count 时。

    IL_0000:  nop

    IL_0001:  ldarg.1    /**//**//**//// 方法参数 source 的值入栈。

    IL_0002:  ldnull    /**//**//**//// “空引用”null入栈。

    IL_0003:  ceq    /**//**//**//// compare equal 比较栈顶的 null 和第二项的 source 是否相等,并

                    /// 把结果 0(false,source 不为空)或 1(true,source 为空)入栈。

    IL_0005:  ldc.i4.0    /**//**//**//// 32 位整型数“0”入栈。

    IL_0006:  ceq    /**//**//**//// 比较栈顶的“0”和堆栈的第二项,第二项可能是“0”,也可能

                    /// 是“1”。比较的结果“1”或“0”入栈。

    IL_0008:  stloc.3    /**//**//**//// 栈顶的“1”或“0”出栈,被保存到索引为“3”的局部变量中。

    IL_0009:  ldloc.3    /**//**//**//// 执行后,栈顶为“1”(source 不为空)或“0”(source 为空)。

    IL_000a:  brtrue.s   IL_000e    /**//**//**//// branch on non-false or non-null 判断栈顶是否

                                    /// 为“1”,如果是,跳转到第“IL_000e”行;否则

                                    /// 继续往下执行。



    IL_000c:  br.s       IL_0036    /**//**//**//// unconditional branch 当栈顶为“0”时,才会

                                    /// 执行到这一行,这一行的执行结果是程序无条件

                                    /// 跳转到第“IL_0036”行。



    IL_000e:  ldarg.1

    IL_000f:  callvirt   instance int32 [mscorlib]System.String::get_Length()    

                        /**//**//**//// 对堆栈最顶上的字符串调用其获取长度的实例方法,长度值被入栈。

                        /// “get_Length()”实际是字符串 Length 属性的“get”部分。

    IL_0014:  stloc.0    /**//**//**//// 局部变量 count 被赋值为字符串长度。

    IL_0015:  ldc.i4.0

    IL_0016:  stloc.2    /**//**//**//// 局部变量 i 被赋值为 0。

    IL_0017:  br.s       IL_002e    /**//**//**//// 无条件跳转到第“IL_002e”行。



    IL_0019:  nop

    IL_001a:  ldarg.1

    IL_001b:  ldloc.2

    IL_001c:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)    

                        /**//**//**//// source 中索引为 i 处的 char 值入栈。

    IL_0021:  stloc.1

    IL_0022:  ldloc.1

    IL_0023:  call       void [mscorlib]System.Console::WriteLine(char)    /**//**//**//// char 值被输

                                                                        /// 出到控制台。

    IL_0028:  nop

    IL_0029:  nop

    IL_002a:  ldloc.2    /**//**//**//// i 值入栈。

    IL_002b:  ldc.i4.1    /**//**//**//// 32 位整数 1 入栈。

    IL_002c:  add    /**//**//**//// i+1 的结果入栈。

    IL_002d:  stloc.2    /**//**//**//// i=i+1。

    IL_002e:  ldloc.2    /**//**//**//// i 值入栈。

    IL_002f:  ldloc.0    /**//**//**//// count 值入栈。

    IL_0030:  clt    /**//**//**//// compare less than 比较 i<count 是否为真,比较结果入栈。

    IL_0032:  stloc.3

    IL_0033:  ldloc.3

    IL_0034:  brtrue.s   IL_0019    /**//**//**//// 如果 i<count 则跳转到第“IL_0019”行。



    IL_0036:  ret

  } // end of method Program::SeparateString





总结分析:

ldstr: 将对字符串的对象引用推送到堆栈上
 ldloc: 将变量的对象引用推送到堆栈上 
 idc.i4   idc.i8:   将4个字节的整数送到堆栈上,将超过32位的常数送到堆栈上
 stloc index: 从堆栈中弹出值并将其放到局部变量index中,index 从0向上编号
 newobj:初始化,调用构造函数,分配内存
 call,callvirt: call 根据引用类型的静态类型来调度方法,callvirt根据引用类型的动态类型来调度方法

 .ctor:构造函数,在类被实例化时,它会被自动调用。

 

 Example 1

class Program           
{         
  static void Main(string[] args)
            {           

       String s = "a";        
                  s = "abcd";           
            }         
}

 IL_0001: ldstr     "a"                   使用ldstr指令为字串对象“a”分配内存,并将此对象引用压入到线程堆栈中
  IL_0006:  stloc.0                           使用stloc指令从线程堆栈顶弹出先前压入的对象引用,将其传给局部变量s(其索引号为0)。
  IL_0007:  ldstr      "abcd"               使用ldstr指令为字串对象“abcd”分配内存,并将此对象引用压入到线程堆栈中
  IL_000c:  stloc.0                            使用stloc指令从线程堆栈顶弹出先前压入的对象引用,将其传给局部变量s(其索引号为0)。

 

 Example 2
(1)String s1 = "ab";          
        s1+="cd";
(2)String s1="ab"+"cd";
 
(1)
         IL_0001: ldstr     "ab"           
         IL_0006:  stloc.0
         IL_0007:  ldloc.0
         IL_0008:  ldstr      "cd"
         IL_000d:  call    string [mscorlib]System.String::Concat(string, string)
         IL_0012:  stloc.0
 (2)
        IL_0001: ldstr     "abcd"
         IL_0006:  stloc.0

可以很清楚地看到,第(1)段代码将导致String类的Concat()方法被调用(实现字串加法运算)。对于第(2)段代码,由于C#编译器聪明地在编译时直接将两个字串合并为一个字串字面量,所以程序运行时CLR只调用一次ldstr指令就完成了所有工作,其执行速度谁快就不言而喻了!

你可能感兴趣的:(代码)