认识IL代码---从开始到现在 <第二篇>
·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是Lutz Roeder开发的一个让人兴奋的反编译利器,目前的版本是Version 5.0.35.0,可以支持.NET3.0,其功能也相当强大,在使用上也较ILDASM更加灵活,如图所示:
可以方便的反编译为IL、C#、VB、Delphi等多种语言,是深入了解IL的最佳利器。
在本文中我们以最简单的ILadsm.exe为说明工具。
3.分析结构
分析IL结构,就参阅《第十三回:从Hello, world开始认识IL》 ,已经有了大致的介绍,在此不需要进行过多的笔墨,实际上IL的本身的结构也不是很复杂,了解了大致的体系即可。
4.解析常用命令
我们在了解了IL文件结构的基础上,通过学习常用的IL命令,就可以基本上对IL达到了了解不过分的标准,因此对IL常用命令的分析就是本文的重点和要点。我们通过对常用命令的解释、示例与分析,逐步了解你陌生的语言世界原来也很简单。
IL指令集包括了基础指令集和对象模型指令集大概有近200多个,对我们来说消化这么多的陌生指令显然不是明智的办法,就行高级语言的关键字一样,我们只取其一瓢独饮,抓大放小的革命传统同样是有效的学习办法,详细的指令集解释请下载[MSIL指令速查手册]。
4.1 newobj和initobj
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 call、callvirt和calli
call、callvirt和calli指令用于完成方法调用,这些正是我们在IL中再熟悉不过的几个朋友。那么,同样是作为方法调用,这几位又有何区别呢?我们首先对其做以概括性的描述,再来通过代码与实例,进入深入分析层面。
·call使用静态调度,也就是根据引用类型的静态类型来调度方法。
·callvirt使用虚拟调度,也就是根据引用类型的动态类型来调度方法;
·calli又称间接调用,是通过函数指针来执行方法调用;对应的直接调用当然就是前面的:call和callvirt。
然而,虽然有以上的通用性结论,但是对于call和callvirt不可一概而论。call在某种情况下可以调用虚方法,而callvirt也可以调用非虚方法。具体的分析我们在以后的文章中来展开,暂不做过多分析。
5.结论
本文从几个重点的IL指令开始,力求通过对比性的分析和深入来逐步揭开IL的神秘与迷惑,正如我们在开始强调的那样,本文只是个开始也许也是个阶段,对IL的探求正如我自己的脚步一样,也在继续着,为的是在.NET的技术世界能够有更多的领悟。作者期望通过不断的努力逐渐和大家一起从IL世界探求.NET世界,在以后的讨论中我们间或的继续这个主题的不断成长。
认识IL代码---从开始到现在 <第二篇>的更多相关文章
- 读懂IL代码就这么简单(三)完结篇
一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认 ...
- 【转载】读懂IL代码就这么简单(三)完结篇
一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认为 ...
- iOS开发:代码通用性以及其规范 第二篇(猜想iOS中实现TableView内部设计思路(附代码),以类似的思想实现一个通用的进度条)
在iOS开发中,经常是要用到UITableView的,我曾经思考过这样一个问题,为什么任何种类的model放到TableView和所需的cell里面,都可以正常显示?而我自己写的很多view却只是能放 ...
- 读懂IL代码(二)
上一篇提到了最基本的IL代码,应该是比较通俗易懂的,所以有了上一篇的基础之后,这篇便要深入一点点的来讲述了. 首先我必须再来说一些重要的概念: Evaluation Stack(评估栈):这是由.NE ...
- IL代码完结篇
读懂IL代码就这么简单(三)完结篇 一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在 ...
- 读懂IL代码就这么简单 ---- IL系列文章
读懂IL代码就这么简单 (一) 读懂IL代码就这么简单(二) 读懂IL代码就这么简单(三)完结篇 出处:http://www.cnblogs.com/zery/tag/IL%20%E7%B3%BB%E ...
- 读懂IL代码就这么简单 (一)
一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉 ...
- 读懂IL代码就这么简单
原文地址:http://www.cnblogs.com/zery/p/3366175.html 一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不 ...
- 读懂IL代码(四)
这一篇是IL系列的最后一篇的,主要是要说一下IL中的流程控制.我相信,经过前面三篇的介绍,看IL代码应该不是什么大问题了吧.好吧,闲话不多说了,就来简单的说一下吧. 还是跟前几篇一样,以例子来解释说明 ...
随机推荐
- POJ 2653 Pick-up sticks(线段相交)
题意:给定n个木棍依次放下,要求最终判断没被覆盖的木棍是哪些. 思路:快速排斥以及跨立实验可以判断线段相交. #include<algorithm> #include<cstdio& ...
- java获得指定日期的前一天,后一天的代码
/** * 获得指定日期的前一天 * @param specifiedDay * @return * @throws Exception */ public static String getSpec ...
- linux环境c++开发:ubuntu12.04使用llvm3.4.2
什么是 clang/llvm/libc++[1] clang 是最近几年(在大财主苹果的支持下)发展得非常好的 C 家族语言 (包括C/C++/Obj-C/Obj-C++) 编译器前端,所谓前端,就是 ...
- 使用ViewPager实现左右“无限”滑动的万年历
有时候就是这样,研究一个问题,一开始想到了一个觉得可行的方案,然后去尝试:尝试了很久.很多次,已经要放弃了,关掉电脑心里 想这个需求没办法实现:在去上厕所的路上突然想到了一个点子,第二天一试,尼玛,搞 ...
- java设计模式--结构型模式--享元模式
享元模式 概述 运用共享技术有效地支持大量细粒度的对象. 适用性 当都具备下列情况时,使用Flyweight模式: 1.一个应用程序使用了大量的对象. 2.完全由于使用大量的对象,造成很大的存储开销. ...
- hmac_sha1 签名
use URI::Escape qw(uri_unescape); use MIME::Base64; use Digest::HMAC_SHA1; $str=Digest::HMAC_SHA1::h ...
- 【转】linux文件系统之mount流程分析
本质上,Ext3 mount的过程实际上是inode被替代的过程. 例如,/dev/sdb块设备被mount到/mnt/alan目录.命令:mount -t ext3 /dev/sdb /mnt/al ...
- android开发步步为营之65:解决ScrollView和ListView触摸事件onInterceptTouchEvent相互冲突问题
近期项目里面有个需求,一个页面放了一个ScrollView,整个页面能够向上滚动,然后ScrollView里面又嵌套了一个ListView,ListView里面的数据也是能够上下滑动的,理论上List ...
- Laravel 安装指南
http://www.golaravel.com/article/laravel-installation-guide/ http://laravel.com/api/4.1/ http://ding ...
- CGContextRef CIImageRef详解
第一种 先用UIImage对象加载一张图片 然后转化成CGImageRef放到CGContext中去编辑 第二种 用CGImageCreate函数创建CGImageRef 然后把CGImageRef放 ...