IL初步了解
一、概述:
近来也是在看AOP方面的东西,了解到Emit可以实现。之前对Emit的了解也就是停留在Reflector针对方法反编译出来的部分指令。就用这次机会学习下Emit也用这篇随笔记录下学习的过程。某些我也不了解的地方也希望各位了解的朋友指导下。
学习前可以先了解下Opcodes
二、工具
1、vs2015
2、.NET Reflector 9.0
三、入门示例
1、输出Hello World
C#代码
static void Main(string[] args) { Console.WriteLine("Hello world!"); }
反编译获取到的IL代码
Emit实现代码
public void HellowWorld() { //定义Hellow方法没有返回值没有参数 DynamicMethod helloWorldMethod = new DynamicMethod("HellowWorld", null, null); //创建IL,动态生成代码 ILGenerator IL = helloWorldMethod.GetILGenerator(); //将输出推送到堆栈上 IL.Emit(OpCodes.Ldstr, "Hello World!"); //执行Console.WriteLine IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //方法结束 IL.Emit(OpCodes.Ret); HelloWordDelegate Method = (HelloWordDelegate)helloWorldMethod.CreateDelegate(typeof(HelloWordDelegate)); Method(); }
委托调用过程中。如果有参数会提示“操作可能破坏运行时稳定性”。(没弄清楚)
四、构建程序集
1、下面通过构建一个类里面包含两个方法来做实例
public int Add(int a, int b) { return a + b; } public string AddList(string[] array) { string result = string.Empty; ; i < array.Length; i++) { result = result + array[i]; } return result; }
2、看下两个方法反编译出来的IL代码
下面我们来看看这段IL到底是如何实现的
●L0000到L0009:将string.Empty赋值给自定义变量resultName,加载整数0,L0009跳转到L001b执行
●L001b到L0027:加载1处索引值,加载参数1(静态从0开始),Ldlen将数组数目从0开始推送到堆栈上,对比两个数值的大小,(将对比结果存储到索引2处,然后再取出(这步实现中可省略)),然后跳转L_000b执行
●L000b到L001a:加载0处索引值,加载参数1,Ldelem_Ref用来加载string类型元素,执行string.concat方法,将值存储到索引0处,加载索引1处值,加载整数1,两值相加,将值存储到索引0处
●L002a结束返回
3、下面我们通过Emit代码来实现
public void GenerateAssembly() { string name = "IL.Dynamic"; string fileName = string.Format("{0}.dll", name); //构建程序集 AssemblyName assemblyName = new AssemblyName(name); //应用程序集域 AppDomain domain = AppDomain.CurrentDomain; //实例化一个AssemblyBuilder对象来实现动态程序集的构建 AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); //定义模块(不加filename为瞬态模块,不持久) ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name); //定义类型 TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public); //定义一个Add方法进行简单的相加 MethodBuilder methodBuilder = typeBuilder.DefineMethod("Add", MethodAttributes.Public, typeof(Int32), new Type[] { typeof(int), typeof(int) }); //IL实现 ILGenerator IL = methodBuilder.GetILGenerator(); IL.Emit(OpCodes.Ldarg_1); IL.Emit(OpCodes.Ldarg_2); IL.Emit(OpCodes.Add); IL.Emit(OpCodes.Ret); //定义一个AddList字符串用for拼接方法 MethodBuilder method2Builder = typeBuilder.DefineMethod("AddList", MethodAttributes.Public| MethodAttributes.Static, typeof(string), new Type[] { typeof(string[]) }); FieldBuilder fieldName = typeBuilder.DefineField("resultName", typeof(string), FieldAttributes.Private | FieldAttributes.Static); ILGenerator addIL = method2Builder.GetILGenerator(); //用来保存求和结果的局部变量 LocalBuilder resultStr = addIL.DeclareLocal(typeof(String)); ////循环中使用的局部变量 LocalBuilder i = addIL.DeclareLocal(typeof(Int32)); Label concatLabel = addIL.DefineLabel(); Label LoopLabel = addIL.DefineLabel(); //设置string result = string.Empty; addIL.Emit(OpCodes.Ldsfld, fieldName); addIL.Emit(OpCodes.Stloc_0); //设置i=0 addIL.Emit(OpCodes.Ldc_I4_0); addIL.Emit(OpCodes.Stloc_1); addIL.Emit(OpCodes.Br, concatLabel); //进入循环体 addIL.MarkLabel(LoopLabel); addIL.Emit(OpCodes.Ldloc_0); //参数指定静态从0开始 addIL.Emit(OpCodes.Ldarg_0); addIL.Emit(OpCodes.Ldloc_1); //Ldelem_Ref用来加载string 类型元素 addIL.Emit(OpCodes.Ldelem_Ref); addIL.Emit(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) })); addIL.Emit(OpCodes.Stloc_0); addIL.Emit(OpCodes.Ldloc_1); //i++ addIL.Emit(OpCodes.Ldc_I4_1); addIL.Emit(OpCodes.Add); addIL.Emit(OpCodes.Stloc_1); addIL.MarkLabel(concatLabel); addIL.Emit(OpCodes.Ldloc_1); addIL.Emit(OpCodes.Ldarg_0); addIL.Emit(OpCodes.Ldlen); addIL.Emit(OpCodes.Conv_I4); //Clt比较两值大小 addIL.Emit(OpCodes.Clt); addIL.Emit(OpCodes.Brtrue_S, LoopLabel); addIL.Emit(OpCodes.Ldloc_0); addIL.Emit(OpCodes.Ret); Type type = typeBuilder.CreateType(); assemblyBuilder.Save(fileName); , , , }; string[] array = new string[] { "a", "b", "c" }; object ob = Activator.CreateInstance(type); , }); var result1 = type.GetMethod("AddList").Invoke(ob, new object[] { array }); }
3.1、AssemblyBuilderAccess
Run | 可以执行但不能保存 |
Save | 保存但不能执行 |
RunAndSave | 可以执行并保存 |
ReflectionOnly | 只反射上下文中加载 |
4、运行输出
4.1、生成文件
借助工具反编译查看IL生成的C#代码
4.2、运行结果
通过这个实例对IL可以有了比较基础的了解,在之后的学习中再慢慢喝大家进行交流。项目下载System.IL
IL初步了解的更多相关文章
- 动手写IL到Lua的翻译器——准备
文章里的代码粘过来的时候格式有点问题,原因是一开始文章是在订阅号上写的(gamedev101,文末有二维码),不知道为啥贴过来就没了格式,还要手动删行号,就没搞了. 介绍下问题背景: 小说君正在参与的 ...
- Ecma335、CLR、CLI、CTS、 IL、.net 以及他们之间的关系
以上是个人对他们直接关系的理解:图片是原创 CLI 通用语言基础架构(Common Language Infrastructure), CLI是一个开放型的技术规范,它定义了一个语言无关的跨体系结构的 ...
- 移动端之Android开发的几种方式的初步体验
目前越来越多的移动端混合开发方式,下面列举的大多数我都略微的尝试过,就初步的认识写个简单的心得: 开发方式 开发环境 是否需要AndroidSDK 支持跨平台 开发语言&技能 MUI Win+ ...
- IL异常处理
异常处理在程序中也算是比较重要的一部分了,IL异常处理在C#里面实现会用到一些新的方法 1.BeginExceptionBlock:异常块代码开始,相当于try,但是感觉又不太像 2.EndExcep ...
- CSharpGL(29)初步封装Texture和Framebuffer
+BIT祝威+悄悄在此留下版了个权的信息说: CSharpGL(29)初步封装Texture和Framebuffer +BIT祝威+悄悄在此留下版了个权的信息说: Texture和Framebuffe ...
- Android自定义View初步
经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下.废话不多说,下面进入我们的正题,首先看一下我们的思路,1.我们需要通过在va ...
- 初步认识Node 之Node为何物
很多人即便是在使用了Node之后也不知道它到底是什么,阅读完本文你应该会有一个初步的.具体的概念了. Node的目标 提供一种简单的构建可伸缩网络程序的方法.那么,什么是可伸缩网络程序呢?可伸缩 ...
- [入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二)
[入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二) Date 周六 10 一月 2015 By 钟谢伟 Category website develop ...
- CLR via C# 摘要二:IL速记
最简单的IL程序 .assembly test {} .method void Func() { .entrypoint ldstr "hello world" call void ...
随机推荐
- Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩
目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...
- 《JavaScript设计模式与开发实践》整理
最近在研读一本书<JavaScript设计模式与开发实践>,进阶用的. 一.高阶函数 高阶函数是指至少满足下列条件之一的函数. 1. 函数可以作为参数被传递. 2. 函数可以作为返回值输出 ...
- iOS UITableView 与 UITableViewController
很多应用都会在界面中使用某种列表控件:用户可以选中.删除或重新排列列表中的项目.这些控件其实都是UITableView 对象,可以用来显示一组对象,例如,用户地址薄中的一组人名.项目地址. UITab ...
- 自定义鼠标光标cursor
通过css属性 Cursor:url()自定义鼠标光标. {cursor:url('图标路径'),default;} url是自定义鼠标图标路径 default指的是定义默认的光标(通常是一个箭头), ...
- IOS之Objective-C学习 ARC下的单例模式
单例模式是我常用的一种设计模式,最常见的用途就是用来保存数据并且传递数据.这都归功于单例模式的特性,首先就让我为大家简单介绍一下单例模式的特性. 单例模式的三大特性: 1.某个类只能有一个实例: 2. ...
- Linux基础介绍【第五篇】
linux权限位 Linux文件或目录的权限位是由9个权限位来控制,每三位为一组,它们分别是文件属主权限.属组权限.其他用户权限. r:read可读权限,对应数字4: w:write可写权限,对应数字 ...
- MyBatis3:SQL映射
前言 前面学习了config.xml,下面就要进入MyBatis的核心SQL映射了,第一篇文章的时候,student.xml里面是这么写的: <?xml version="1.0&qu ...
- 小谈Scrum敏捷开发流程
一晃眼,有两年没有写博客了,回顾前两年,各种奔波,各种忙碌,也有不少的收获.从今天开始,我要把这些收获都分享在这里. 其实这两年,对我影响最大的是开发流程.总所周知,一个好的开发流程,对于项目的进行, ...
- 如何用Dockerfile创建镜像
本文原创,原文地址为:http://www.cnblogs.com/fengzheng/p/5181222.html 创建镜像的目的 首先说DockerHub或其它一些镜像仓库已经提供了够多的镜像,有 ...
- C# 开发windows服务的一些心得
最近在做一个windows服务的项目,发现并解决了一些问题,拿出来和大家分享一下,以下windows服务简称“服务” 文章会在适合时间更新,因为朋友们在不断提出新的意见或思路,感谢-.- 1.服务如何 ...