浅析.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 SDKs\Windows\v7.0A\bin\ildasm.exe

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

  

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

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

  

三、分析IL代码

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

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的掌握的程度

答案后续公布。

时间: 2024-10-08 03:15:08

浅析.NET IL代码的相关文章

【小白学C#】浅谈.NET中的IL代码

一.前言 前几天群里有位水友提问:”C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据还是从复制的函数栈中拿数据“.其实很明显,这和方法参数的传递方式有关,如果是引用传递的话,肯定是会去静态字段直接拿值的:如果方法是以传值的方式使用参数的话,一定是从复制的栈中拿值的. 但是这位水友就是不相信这个结论(后来发现,这货整一个杠精啊!天天在群里跟人抬杠~),这个时候我忽然想到了可以反向看一下C#的IL代码,来了解一下程序运行的过程.的确,当我们对运行结果有异议的时候,可以通

CLR基础,CLR运行过程,使用dos命令创建、编译、运行C#文件,查看IL代码

CLR是Common Language Runtime的缩写,是.NET程序集或可执行程序运行的一个虚拟环境.CLR用于管理托管代码,但是它本身是由非托管代码编写的,并不是一个包含了托管代码的程序集,所以不能使用IL DASM进行查看,但CLR以dll的形式位于.NET版本号文件夹内. □ C#源代码从编译到CLR运行的全过程 →编写C#源代码,以class,struct,enum,interface,delegate...的形式 →编译器把源代码编译成.dll或.exe,其中包含了一些重要信息

认识IL代码---从开始到现在 <第二篇>

·IL代码分析方法 ·IL命令解析 ·.NET学习方法论 1.引言 自从『你必须知道.NET』系列开篇以来,受到大家很多的关注和支持,给予了anytao巨大的鼓励和动力.俱往昔,我发现很多的园友都把目光和焦点注意在如何理解IL代码这个问题上.对我来说,这真是个莫大的好消息,因为很明显我们的思路慢慢的从应用向底层发生着转变,技巧性的东西是一个方面的积累,底层的探索在我认为也是必不可少的修炼.如果我们选择了来关注这项修炼,那么我们就应该选择如何来着手这项修炼,首先关注anytao的『你必须知道的.N

读懂IL代码(二)

上一篇提到了最基本的IL代码,应该是比较通俗易懂的,所以有了上一篇的基础之后,这篇便要深入一点点的来讲述了. 首先我必须再来说一些重要的概念: Evaluation Stack(评估栈):这是由.NET CLR在执行时候自动管理的记忆体,每一个线程都有自己的评估栈,也就是说,它是用来存储临时变量的线程栈(应该可以这么理解).值类型存储数据,引用类型存储地址. Call Stack(调用栈):这也是由.NET CLR在执行时候自动管理的记忆体,每一个线程都有自己的调用栈,每一次调用method,就

C#程序集系列01,用记事本编写C#,IL代码,用DOS命令编译程序集,运行程序

本篇主要体验:编写C#,IL代码,用"VS2012开发人员命令提示"编译成程序集,并运行程序. □ C#文件编译为程序集 →在F盘创建as文件夹→在as文件夹下创建MyClass.cs→用记事本打开编写如下代码,并保存 using System; public class MyClass { public static void PrintSth() { Console.WriteLine("Hello"); } } →打开"VS2012开发人员命令提示&

C#程序集系列02,使用记事本查看可执行程序集的IL代码

继续上一篇"C#程序集系列01,用记事本编写C#,IL代码,用DOS命令编译程序集,运行程序",在F盘的as文件夹中已经有了若干程序集.本篇体验使用记事本查看可执行程序集的IL代码. →打开"VS2012开发人员命令提示",输入如下命令,并按回车在as文件夹中多了1.txt和1.res这2个文件→输入如下命令打开1.txt // Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020

读懂IL代码就这么简单(一)

一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉厉. 然后开始接触IL,了解了一段时后才发现原来读懂IL代码并不难.进入正题   1.1  什么是IL IL是.NET框架中中间语言(Intermediate Language)的缩写.使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直

读懂IL代码就这么简单(二)

一 前言 IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致.个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 .感谢 @冰麟轻武 的指点 你没有看第一篇?  点这里看第一篇 读懂IL代码就这么简单(一) IL指令大全 :IL指令详解 IL反编译工具: ILDasm 知识点回顾: Managed Heap(托管堆):用于存放引用类型的值 Evaluation Statck(计算栈):临时存放值类型数据,引用

用ildasm/ilasm修改IL代码

原文地址:http://www.cnblogs.com/dudu/archive/2011/05/17/ildasm_ilasm_il.html 在开发中遇到这样一个场景,需要修改一个dll文件(.NET程序集)中某些地方的类型名称,但没有源代码,只能修改IL代码. 操作步骤如下: 1. 运行ildasm ildasm是由微软提供的.NET程序反编译工具,位于"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\". 2. 用ildasm