• 详解.NET IL代码


    一、前言

    IL是什么?

     Intermediate Language (IL)微软中间语言

    C#代码编译过程?

    C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令;

    JIT编译器把IL代码转为机器识别的机器代码。如下图

    语言编译器:无论是VB code还是C# code都会被Language Compiler转换为MSIL

    MSIL的作用:MSIL包含一些元数据和中间语言指令

    JIT编译器的作用:根据系统环境将MSIL中间语言指令转换为机器码

    为什么ASP.NET网站第一次运行时会较慢,而后面的执行速度则会相对快很多?

    当你第一次运行.NET开发的站点时,CLR会将MSIL通过JIT进行编译,最终转换为执行速度非常快的Native Code。这可以解释。

    为什么要了解IL代码?

    如果想学好.NET,IL是必须的基础,IL代码是.NET运行的基础,当我们对运行结果有异议的时候,可以通过IL代码透过表面看本质;

    IL也是更好理解、认识CLR的基础;

    大量的实例分析是以IL为基础的,所以了解IL,是读懂他人代码的必备基础,同时自己也可以获得潜移默化的提高;

    二、如何把ILDasm导入到VS中

    想要看IL代码需要使用ILDasm工具,工具一般在电脑的

    C:Program Files (x86)Microsoft SDKsWindowsv7.0Ainildasm.exe

    也可以下载ILSpy:http://ilspy.net/

    把ILDasm导入到VS工具中,使用方便,具体如下:工具 - > 外部工具 

      

    导入之后,vs工具里面就有ILDasm工具了。以后想看IL代码方便多了。

    IL代码通过ILDasm反编译后(左图),ILDasm图标意义(右图)

      

    三、分析IL代码

    在分析IL代码之前,要先理解几个概念:

    图片来源:https://msdn.microsoft.com/zh-tw/library/dd229210.aspx

    Managed Heap(托管堆):这就是NET中的托管堆,用来存放引用类型,它是由GC(垃圾回收器自动进行回收)管理;

    Call Stack(调用堆栈):调用堆栈:调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法。

    Evaluation Stack(计算堆栈):每个线程都有自己的线程栈,IL 里面的任何计算,都发生在 Evaluation Stack 上,其实就是一个 Stack 结构。可以 Push,也可以 Pop。

    可以对照IL指令:指令列表,一步一步来分析IL代码

    1、用C#写一个简单控制台应用程序

    using System;
    
    namespace ILDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                int i = 1;
                int j = 2;
                int k = 3;
                int answer = i + j + k;
                Console.WriteLine("i+j+k=" + answer);
                Console.ReadKey();
            }
        }
    }

    2、 用ILDasm打开bin下的.exe文件查看代码,具体IL代码如下:

    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint 
      // 代码大小       42 (0x2a)
      .maxstack  2
      .locals init ([0] int32 i,
               [1] int32 j,
               [2] int32 k,
               [3] int32 answer)
      IL_0000:  nop
      IL_0001:  ldc.i4.1
      IL_0002:  stloc.0
      IL_0003:  ldc.i4.2
      IL_0004:  stloc.1
      IL_0005:  ldc.i4.3
      IL_0006:  stloc.2
      IL_0007:  ldloc.0
      IL_0008:  ldloc.1
      IL_0009:  add
      IL_000a:  ldloc.2
      IL_000b:  add
      IL_000c:  stloc.3
      IL_000d:  ldstr      "i+j+k="
      IL_0012:  ldloc.3
      IL_0013:  box        [mscorlib]System.Int32
      IL_0018:  call       string [mscorlib]System.String::Concat(object,
                                                                  object)
      IL_001d:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_0022:  nop
      IL_0023:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
      IL_0028:  pop
      IL_0029:  ret
    } // end of method Program::Main

    3、会用到的IL指令:

    nop:无操作

    ret:从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。

    call:调用由传递的方法说明符指示的方法。

    box:将值类转换为对象引用,就是装箱,同理可以知道拆箱unbox

    ldc.i4.X:把int32的值推送到计算堆栈

    stloc.X:把计算堆栈顶部的值放到调用堆栈索引为X处

    ldloc.X:把调用堆栈X处的值复制到计算堆栈

    4、理解注释后的代码

    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint  //程序入口
      // 代码大小       42 (0x2a)
      .maxstack  2    // 计算出计算堆栈的能存几个值
    
      .locals init ([0] int32 i,
               [1] int32 j,
               [2] int32 k,
               [3] int32 answer) //定义int32类型的i,j,k,answer
    
      IL_0000:  nop   //无操作
    
      IL_0001:  ldc.i4.1  //把i的值放到计算堆栈上
      IL_0002:  stloc.0   //把计算堆栈顶部的值(i的值)放到调用堆栈索引0处
      IL_0003:  ldc.i4.2  //把j的值放到计算堆栈上
      IL_0004:  stloc.1   //把计算堆栈顶部的值(j的值)放到调用堆栈索引1处
      IL_0005:  ldc.i4.3  //把k的值放到计算堆栈上
      IL_0006:  stloc.2   //把计算堆栈顶部的值(k的值)放到调用堆栈索引2处
    
      IL_0007:  ldloc.0   //把调用堆栈索引为0处的值复制到计算堆栈 
      IL_0008:  ldloc.1   //把调用堆栈索引为1处的值复制到计算堆栈
      IL_0009:  add       //相加
      IL_000a:  ldloc.2   //把调用堆栈索引为2处的值复制到计算堆栈
      IL_000b:  add       //相加
      IL_000c:  stloc.3   //把计算堆栈顶部的值(add的值)放到调用堆栈索引3处
      IL_000d:  ldstr      "i+j+k="  //推送对元数据中存储的字符串的新对象引用。
      IL_0012:  ldloc.3   //把调用堆栈索引为3处的值复制到计算堆栈
    
      IL_0013:  box        [mscorlib]System.Int32     //装箱
      IL_0018:  call       string [mscorlib]System.String::Concat(object,object)   //调用内部方法
      IL_001d:  call       void [mscorlib]System.Console::WriteLine(string)       //调用WriteLine
      IL_0022:  nop       //无操作
      IL_0023:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()  //调用ConsoleKey
      IL_0028:  pop      //无操作
      IL_0029:  ret      //return
    } // end of method Program::Main

    四、最后

    IL是跟我高级架构经理的分享和博客园相关的博客学习总结的,最后留2个题目,也是我的高级架构经理分享中提到的两个好玩的问题,也能看出对IL的掌握的程度

     答案后续公布。

  • 相关阅读:
    maven的groupId和artifactId的区别
    二级域名和一级域名的区别
    单点登陆系统
    公有链,私有链,联盟链
    比特币与区块链是父与子关系吗
    比特币钱包:核心钱包与轻钱包
    区块链到底是什么
    打造信任机器——区块链技术及其应用
    区块广播
    区块链技术理念
  • 原文地址:https://www.cnblogs.com/yinrq/p/5486103.html
Copyright © 2020-2023  润新知