一、概述:

近来也是在看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初步了解的更多相关文章

  1. 动手写IL到Lua的翻译器——准备

    文章里的代码粘过来的时候格式有点问题,原因是一开始文章是在订阅号上写的(gamedev101,文末有二维码),不知道为啥贴过来就没了格式,还要手动删行号,就没搞了. 介绍下问题背景: 小说君正在参与的 ...

  2. Ecma335、CLR、CLI、CTS、 IL、.net 以及他们之间的关系

    以上是个人对他们直接关系的理解:图片是原创 CLI 通用语言基础架构(Common Language Infrastructure), CLI是一个开放型的技术规范,它定义了一个语言无关的跨体系结构的 ...

  3. 移动端之Android开发的几种方式的初步体验

    目前越来越多的移动端混合开发方式,下面列举的大多数我都略微的尝试过,就初步的认识写个简单的心得: 开发方式 开发环境 是否需要AndroidSDK 支持跨平台 开发语言&技能 MUI Win+ ...

  4. IL异常处理

    异常处理在程序中也算是比较重要的一部分了,IL异常处理在C#里面实现会用到一些新的方法 1.BeginExceptionBlock:异常块代码开始,相当于try,但是感觉又不太像 2.EndExcep ...

  5. CSharpGL(29)初步封装Texture和Framebuffer

    +BIT祝威+悄悄在此留下版了个权的信息说: CSharpGL(29)初步封装Texture和Framebuffer +BIT祝威+悄悄在此留下版了个权的信息说: Texture和Framebuffe ...

  6. Android自定义View初步

    经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下.废话不多说,下面进入我们的正题,首先看一下我们的思路,1.我们需要通过在va ...

  7. 初步认识Node 之Node为何物

    很多人即便是在使用了Node之后也不知道它到底是什么,阅读完本文你应该会有一个初步的.具体的概念了.    Node的目标 提供一种简单的构建可伸缩网络程序的方法.那么,什么是可伸缩网络程序呢?可伸缩 ...

  8. [入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二)

    [入门级] 基于 visual studio 2010 mvc4 的图书管理系统开发初步 (二) Date  周六 10 一月 2015 By 钟谢伟 Category website develop ...

  9. CLR via C# 摘要二:IL速记

    最简单的IL程序 .assembly test {} .method void Func() { .entrypoint ldstr "hello world" call void ...

随机推荐

  1. 自己实现一个javascript事件模块

    nodejs中的事件模块 nodejs中有一个events模块,用来给别的函数对象提供绑定事件.触发事件的能力.这个别的函数的对象,我把它叫做事件宿主对象(非权威叫法),其原理是把宿主函数的原型链指向 ...

  2. SQL Server 2016白皮书

    随着SQL Server 2016正式版发布日临近,相关主要特性通过以下预览学习: Introducing Microsoft SQL Server 2016 e-bookSQL Server 201 ...

  3. [原] KVM 虚拟化原理探究(3)— CPU 虚拟化

    KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...

  4. Java 程序优化 (读书笔记)

    --From : JAVA程序性能优化 (葛一鸣,清华大学出版社,2012/10第一版) 1. java性能调优概述 1.1 性能概述 程序性能: 执行速度,内存分配,启动时间, 负载承受能力. 性能 ...

  5. H3 BPM让天下没有难用的流程之技术体系

    一.技术架构 H3 BPM 基于微软.NET 技术架构,采用C#语言开发,以高开放.高扩展.高性能为核心准则,遵循分层的设计原理,结合最新的B/S 以及智能手机应用开发技术研发的. 图:H3 BPM  ...

  6. Android使用静默安装时碰见的问题

    升级时碰见的异常 private void installPackage(String appName,final File apk) { if (!apk.exists()) { setHasNew ...

  7. MongoDB学习笔记二—Shell操作

    数据类型 MongoDB在保留JSON基本键/值对特性的基础上,添加了其他一些数据类型. null null用于表示空值或者不存在的字段:{“x”:null} 布尔型 布尔类型有两个值true和fal ...

  8. 分享在Linux下使用OSGi.NET插件框架快速实现一个分布式服务集群的方法

    在这篇文章我分享了如何使用分层与模块化的方法来设计一个分布式服务集群.这个分布式服务集群是基于DynamicProxy.WCF和OSGi.NET插件框架实现的.我将从设计思路.目标和实现三方面来描述. ...

  9. 为Xamarin更好的开发而改写的库

    欢迎大家加入以下开源社区 Xamarin-Cn:https://github.com/Xamarin-Cn Mvvmcross-Cn:https://github.com/Mvvmcross-Cn  ...

  10. AutoMapper(七)

    返回总目录 Null值替换 如果源类型的成员链上的属性值为Null,Null值替换允许提供一个可替换的值.下面有两个类Person和PersonInfo类,都有一个属性Title(头衔),从Perso ...