本文将介绍以下内容:

·       IL代码分析方法

·       IL命令解析

·       .NET学习方法论


1. 
引言

自从『你必须知道.NET』系列开篇以来,受到大家很多的关注和支持,给予了anytao巨大的鼓励和动力。俱往昔,我发现很多的园友都把目光和焦点注意在如何理解IL代码这个问题上。对我来说,这真是个莫大的好消息,因为很明显我们的思路慢慢的从应用向底层发生着转变,技巧性的东西是一个方面的积累,底层的探索在我认为也是必不可少的修炼。如果我们选择了来关注这项修炼,那么我们就应该选择如何来着手这项修炼,首先关注anytao的『你必须知道的.NET』系列可以给你提供一个捷径,少花一些功夫;其次对大师级的作品也应有更深入的了解,如《Applied Microsoft .NET Framework Programming》、《.NET本质论》;再次,就是像我一样从博客园和MSDN的知识库中不断的成长。呵呵,除了给自己做了个广告之外,我认为不管是何种途径,了解和认识IL代码,对于我们更深刻的理解.NET和.NET应用之上的本质绝对有不一样的收获,这也就是本文研究和分享的理由。

那么,我们要了解IL代码,就要知道了解IL的好处,时间对每个程序设计师来说都是宝贵的,你必须清楚自己投资的价值再决定投入的资本。对于.NET程序员来说,IL代码意味着:

·       通用的语言基础是.NET运行的基础,当我们对程序运行的结果有异议的时候,如何透过本质看表面,需要我们从本质入手来探索,这时IL是你必须知道的基础;

·       元数据和IL语言是CLR的基础,了解必要的中间语言是深入认识CLR的捷径;

·       大量的事例分析是以IL来揭密的,因此了解IL是读懂他人代码的必备基础,可以给自己更多收获。

很明显这些优越性足以诱惑我们花时间和精力涉猎其中。然而,了解了IL的好处,并不意味着我们应该过分的来关注IL,有人甚至可以洋洋洒洒的写一堆IL代码来实现一个简单Hello world程序,但是正如我们知道的那样,程序设计已经走过了几十年的发展,如果纯粹的陶醉在历史中,除了脑子不好,没有其他的解释。不然看见任何代码都以IL的角度来分析,又将走进另一个误区,我们的宗旨是追求但不过分。

因此,有了上述了应该了解的理由和不应该过分的基线,在摆正心态的前提下,本文开始以作者认为的方式来展开对IL代码的认识,作者期望通过本文的阐述与分析使得大家都能对IL有个概观之解,并在平时的项目实践中使用这种方法通过了解自己的代码来了解.NET。我想,这种方法应该是值得提倡和发挥的最佳实践,不知你信不信呢?呵呵。

2. 使用工具

俗话说,工欲善其事,必先利其器。IL的器主要就是ILadsm.exe和reflector.exe,这两个工具都是了解IL的基础,其原理都是通过反射机制来查看IL代码。

·       ILadsm.exe

打开.NET Framework SKD 命令提示行,输入ildasm回车即可打开,如图所示:

上图是我们熟悉的《第十三回:从Hello, world开始认识IL》中的示例,其中的树形符号代表的意思,可以从MSDN的一张经典帮助示例来解释,如下图所示:


(图表来源:MSDN)

·       reflector.exe【下载】

Reflector是Lutz Roeder开发的一个让人兴奋的反编译利器,目前的版本是Version 5.0.35.0,可以支持.NET3.0,其功能也相当强大,在使用上也较ILDASM更加灵活,如图所示:

Reflector可以方便的反编译为IL、C#、VB、Delphi等多种语言,是深入了解IL的最佳利器。

在本文中我们以最简单的ILadsm.exe为说明工具。

3. 分析结构

分析IL结构,就参阅《第十三回:从Hello, world开始认识IL》 ,已经有了大致的介绍,在此不需要进行过多的笔墨,实际上IL的本身的结构也不是很复杂,了解了大致的体系即可。

4. 解析常用命令

我们在了解了IL文件结构的基础上,通过学习常用的IL命令,就可以基本上对IL达到了了解不过分的标准,因此对IL常用命令的分析就是本文的重点和要点。我们通过对常用命令的解释、示例与分析,逐步了解你陌生的语言世界原来也很简单。

IL指令集包括了基础指令集和对象模型指令集大概有近200多个,对我们来说消化这么多的陌生指令显然不是明智的办法,就行高级语言的关键字一样,我们只取其一瓢独饮,抓大放小的革命传统同样是有效的学习办法,详细的指令集解释请下载[MSIL指令速查手册]。

4.1 newobjinitobj

newobj和intiobj指令就像两个兄弟,常常让我们迷惑在其然而不知其所以然,虽然认识但是不怎么清楚,这种感觉很郁闷,下面就让我们看看他们的究竟:

代码引入


指令说明


深入分析

从上面的代码中,我们可以得出哪些值得推敲的结论呢?

MSDN给出的解释是:newobj用于分配和初始化对象;而initobj用于初始化值类型。

那么newobj又是如何分配内存,完成对象初始化;而initobj又如何完成对值类型的初始化呢?

显然,关于newobj指令,在《第五回:深入浅出关键字---把NEW说透》中,已经有了一定的介绍,简单说来关于newobj我们有如下结论:

·       从托管堆分配指定类型所需要的全部内存空间。

·       在调用执行构造函数初始化之前,首先初始化对象附加成员:一个是指向该类型方法表的指针;一个是SyncBlockIndex,用于进行线程同步。所有的对象都包含这两个附加成员,用于管理对象。

·       最后才是调用构造函数ctor,进行初始化操作。并返回新建对象的引用地址。

而initobj的作用又可以小结为:

·       构造新的值类型,完成值类型初始化。值得关注的是,这种构造不需要调用值类型的构造函数。具体的执行过程呢?以上例来说,initobj MyStruct的执行结果是,将MyStruct中的引用类型初时化为null,而基元类型则置为0。

因此,值类型的初始化可以是:

//initobj方式初始化值类型

initobj    Anytao.net.My_Must_net.IL.MyStruct

同时,也可以直接显示调用构造函数来完成初始化,具体为

MyStruct ms = new MyStruct(123);

对应于IL则是对构造函数cto的调用。

//调用构造函数方式初始化值类型

call       instance void Anytao.net.My_Must_net.IL.MyStruct::.ctor(int32)

·       Initobj还用于完成设定对指定存储单元的指针置空(null)。这一操作虽不常见,但是应该引起注意。

由此可见,newobj和initobj,都具有完成实例初始化的功能,但是针对的类型不同,执行的过程有异。其区别主要包括:

·       newobj用于分配和初始化对象;而initobj用于初始化值类型。因此,可以说,newobj在堆中分配内存,并完成初始化;而initobj则是对栈上已经分配好的内存,进行初始化即可,因此值类型在编译期已经在栈上分配好了内存。

·       newobj在初始化过程中会调用构造函数;而initobj不会调用构造函数,而是直接对实例置空。

·       newobj有内存分配的过程;而initobj则只完成数据初始化操作。

关于对象的创建,还有其他的情况值得注意,例如:

·       Newarr指令用来创建一维从零起始的数组;而多维或非从零起始的一维数组,则仍由newobj指令创建。

·       String类型的创建由ldstr指令来完成,具体的讨论我们在下文来展开。

4.2 callcallvirtcalli

call、callvirt和calli指令用于完成方法调用,这些正是我们在IL中再熟悉不过的几个朋友。那么,同样是作为方法调用,这几位又有何区别呢?我们首先对其做以概括性的描述,再来通过代码与实例,进入深入分析层面。

·       call使用静态调度,也就是根据引用类型的静态类型来调度方法。

·       callvirt使用虚拟调度,也就是根据引用类型的动态类型来调度方法;

·       calli又称间接调用,是通过函数指针来执行方法调用;对应的直接调用当然就是前面的:call和callvirt。

然而,虽然有以上的通用性结论,但是对于call和callvirt不可一概而论。call在某种情况下可以调用虚方法,而callvirt也可以调用非虚方法。具体的分析我们在以后的文章中来展开,暂不做过多分析。

5. 结论

本文从几个重点的IL指令开始,力求通过对比性的分析和深入来逐步揭开IL的神秘与迷惑,正如我们在开始强调的那样,本文只是个开始也许也是个阶段,对IL的探求正如我自己的脚步一样,也在继续着,为的是在.NET的技术世界能够有更多的领悟。作者期望通过不断的努力逐渐和大家一起从IL世界探求.NET世界,在以后的讨论中我们间或的继续这个主题的不断成长。


[
下载]

reflector.exe

IL指令速查手册(感谢温少提供)

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

(USA)David Chappell, Understanding .NET

[公告]

【CLR团队公告】活动公告、邀请函、团队纲领

【系列公告】从架构到设计、你必须知道的.NET

温故知新

[开篇有益]
[第一回:恩怨情仇:is和as]
[第二回:对抽象编程:接口和抽象类]
[第三回:历史纠葛:特性和属性]
[第四回:后来居上:class和struct]
[第五回:深入浅出关键字---把new说透]
[第六回:深入浅出关键字---base和this]
[第七回:品味类型---从通用类型系统开始]
[第八回:品味类型---值类型与引用类型(上)-内存有理]
[第九回:品味类型---值类型与引用类型(中)-规则无边]
[第十回:品味类型---值类型与引用类型(下)-应用征途]
[第十一回:参数之惑---传递的艺术(上)]
[第十二回:参数之惑---传递的艺术(下)]
[第十三回:从Hello, world开始认识IL]

[你必须知道的.NET]第十四回:认识IL代码---从开始到现在相关推荐

  1. [你必须知道的.NET]第二十四回:认识元数据和IL(上)

    说在,开篇之前 很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有<第一回:恩怨情仇:is和as>那么 ...

  2. [你必须知道的.NET]第二十六回:认识元数据和IL(下)

    说在,开篇之前 书接上回:  第二十四回:认识元数据和IL(上), 第二十五回:认识元数据和IL(中) 我们继续. 终于到了,说说元数据和IL在JIT编译时的角色了,虽然两个回合的铺垫未免铺张,但是却 ...

  3. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)],我们对PE文件.程序集.托管模块,这些概念与元数据.IL的关系进行了必要的铺垫,同时顺便熟悉了以ILDASM工具进行反编译的基本方法认知 ...

  4. [你必须知道的.NET]第二十九回:.NET十年(上)

    引言 语言是程序开发者行走江湖的手上利器,各大门派的高手在论坛.博客为了自家门派争吵不已早是技术世界中的亮丽风景,虽多少为刚刚踏入江湖的新手提供了思考的素材,但也同时迷惑了初出茅庐的前行方向. 本文不 ...

  5. [你必须知道的.NET]第二十八回:说说Name这回事儿

    1 缘起 老赵在谈表达式树的缓存(2):由表达式树生成字符串中提到,在描述Type信息时讨论FullName或者AssemblyQualifiedName提供完整的Type信息,虽是小话题,但却是值得 ...

  6. [你必须知道的.NET]第十六回:深入浅出关键字---using全接触

    本文将介绍以下内容: using指令的多种用法 using语句在Dispose模式中的应用 1. 引言 在.NET大家庭中,有不少的关键字承担了多种角色,例如new关键字就身兼数职,除了能够创建对象, ...

  7. [你必须知道的.NET]第十九回:对象创建始末(下)

    本文将介绍以下内容: 对象的创建过程 内存分配分析 内存布局研究 接上回[第十八回:对象创建始末(上)],继续对对象创建话题的讨论>>> 2.2 托管堆的内存分配机制 引用类型的实例 ...

  8. [你必须知道的.NET]第十八回:对象创建始末(上)

    本文将介绍以下内容: 对象的创建过程 内存分配分析 内存布局研究 1. 引言 了解.NET的内存管理机制,首先应该从内存分配开始,也就是对象的创建环节.对象的创建,是个复杂的过程,主要包括内存分配和初 ...

  9. [你必须知道的.NET]第十五回:继承本质论

    本文将介绍以下内容: 什么是继承? 继承的实现本质 1. 引言 关于继承,你是否驾熟就轻,关于继承,你是否了如指掌. 本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的运 ...

最新文章

  1. 职场笔记[0702]:用户体验和创业
  2. ZooKeeper场景实践:(6)集群监控和Master选举
  3. 请问这个解法的时间复杂度怎么分析?谢谢!
  4. jquery 数组的调用
  5. 京东向下沉市场打出“京喜”,拼多多和阿里惊不惊喜?
  6. 万字长文梳理:从0开始,步入Service Mesh微服务架构的世界
  7. Problem H: 今年第几天?
  8. 面试官问:断网了,还能ping通 127.0.0.1 吗?为什么?
  9. 断路器(CircuitBreaker)设计模式
  10. 论高校计算机信息管理能力的提升,论高校计算机信息管理能力的提升
  11. 有哪些闷声发大财的行业?
  12. 移动端前端月历日历_前端基础:必须要知道的移动端适配(2)——分辨率
  13. linux防火墙保存报错,29.Linux防火墙-firewalled
  14. 左程云《程序员代码面试指南》C++实现
  15. 耳机插在电脑上没声音linux,解决linux上耳机没有声音
  16. 写给很累的你:面对苦难,停止内耗
  17. OV7670 FIFO 30W摄像头介绍(三) --- STM32F103驱动OV7670代码介绍
  18. Google账号登录后直接跳转百度首页,登陆不上
  19. Matlab学习笔记9.3:Matlab之神经网络模型
  20. uilabel 自行撑开高度_textarea高度随内容自适应增高撑开

热门文章

  1. Fiddler 学习笔记---命令、断点
  2. hdoj-3342-Legal or Not(拓扑排序)
  3. JavaScript——执行环境、变量对象、作用域链
  4. SQLServer 的存储过程与java交互
  5. Python之异常追踪模块:traceback
  6. c# 路径空格---ProcessStartInfo参数问题
  7. 【转】excel表格导出集锦repeater实用,和普通用法
  8. templatefield 动态_GridView动态创建templateField | 学步园
  9. php为什么搜不到字段,php-通过多个字段搜索.有时一个领域,有时不止一个领域
  10. centos安装无线网卡驱动_CentOS下显卡驱动安装的相关思考