`

Emit学习之旅(1):Emit概览

    博客分类:
  • C#
阅读更多

转载Frog 博客空间:http://www.cnblogs.com/MythYsJh/archive/2010/04/19/1715383.html

 

一、Emit概述

  Emit,可以称为发出或者产生。在Framework中,与Emit相关的类基本都存在于System.Reflection.Emit命名 空间下。可见Emit是作为反射的一个元素存在的。说道反射,大家应该都不陌生,它允许我们查看程序集的元素据,从而取得形如程序集包含哪些类型,类型包 含哪些方法等等大量的信息。但是反射也仅能够‘看’,而Emit则可以在运行时动态生成代码。接下来就来看看如何用Emit生成代码。

二、动态生成代码

  首先需要明确的是这里的代码并不是我们时常提到的C#,VB等源代码,而是IL代码。既然是IL代码,那学习Emit是不是要先对IL很熟悉 呢?诚然,熟悉IL代码对Emit学习会大有帮助,但是不懂也没关系,因为IL和高级语言一样,也是有一些相对固定的语法结构组成,不可能在一个IL程序 里表述if是一个样子而到另一个程序却变成了另一个样子。所以只要多用,多记,很快就能掌握这些东西。

  其次如C#,VB等程序会包含程序集,模块,类,方法,属性等元素一样,Emit生成的代码也包括这些元素。以下介绍Emit生成代码的基本流程:

 

  1.构建程序集

  在创建程序集之前,我们先要为它取个名字。

var asmName = new AssemblyName("Test");

     AssemblyName位于System.Reflection命名空间下,它代表程序集的名称。

  然后我们就可以用上面的名字来创建一个程序集了:

 

var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);

 

AssemblyBuilderAccess.ReflectionOnly:
  DefineDynamicAssembly
有很多重载,比如上面的例子可以添加第三个参数用于作为生成的程序集要存放到的目录。关于其他重载形式,大家可以查阅MSDN。这里重点说说
AssemblyBuilderAccess这个枚举。
  它有以下几个值:
  AssemblyBuilderAccess.ReflectionOnly:表示动态程序集只能用于反射获取元素据用,不能执行。
  AssemblyBuilderAccess.Run:表示动态程序集是用于执行的。
  AssemblyBuilderAccess.Save:表示动态程序集会被保存到磁盘上,不能立即执行。
  AssemblyBuilderAccess.RunAndSave:表示动态程序集会被保存至磁盘并能立即执行。

 

  2.创建模块

  创建程序集后,就需要为程序集添加模块了,我们可以如下定义一个模块:

 

var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.dll");

     如果想把动态生成的程序集保存至磁盘(如本例),定义模块时模块所在文件的名称一定要和保存程序集(后面会提到)时提供的文件名称一样。

 

  3.定义类

  有了前面的准备工作,我们开始定义我们的类型:

var typeBldr = mdlBldr.DefineType("Hello",TypeAttributes.Public);

    DefineType还可以设置要定义的类的基类,要实现的接口等等。

 

  4.定义类成员(方法,属性等等)

  既然有了类,下面我们就为它添加一个SayHello方法吧:

var methodBldr = typeBldr.DefineMethod("SayHello", 
                MethodAttributes.Public, 
                null,//return type
                null//parameter type
                );

    该方法的原型为public void SayHell();

 

  方法签名已经生成好了,但方法还缺少实现。在生成方法的实现前,必须提及一个很重要的概念:evaluation stack。在.Net下基本所有的操作都是通过入栈出栈完成的。这个栈就是evaluation stack。比如要计算两个数(a,b)的和,首先要将a放入evaluation stack中,然后再将b也放入栈中,最后执行加法时将弹出栈顶的两个元素也就是a和b,相加再将结果推送至栈顶。

  Console.WriteLine("Hello,World")可以用Emit这样生成:

var il = methodBldr.GetILGenerator();//获取il生成器
il.Emit(OpCodes.Ldstr,"Hello, World");
il.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(string)}));
il.Emit(OpCodes.Ret);

    OpCodes枚举定义了所有可能的操作,这里用到了:

 

  ldStr:加载一个字符串到evaluation stack。

  Call:调用方法。

  Ret:返回,当evaluation stack有值时会返回栈顶值。

  完成上面的步骤,一个类型好像就已经完成了。事实上却还没有,最后我们还必须显示的调用CreateType来完成类型的创建。

 

typeBldr.CreateType();

     这样一个完整的类就算完成了。但为了能用reflector查看我们创建的动态程序集,我们选择将这个程序集保存下来。

asmBuilder.Save("Main.dll");

   如前面定义模块时所说,这里文件名字必须和模块保存到的文件一致,否则我们前面定义的模块和这个模块的一切就无家可归了。接下来,(如果在定义模块时未指 定动态创建的程序要保存到哪个目录)我们就可以到 Debug目录下看看生成的Main.dll了,用Reflector打开可以看到:

   

  大功告成。

 

     三、不包含main的控制台程序

  一直以来,应用程序(控制台,winform)都是从Main函数启动的,如果没有Main还能启动吗?答案是可以,下面就用emit来做这样一个控制台程序,完整代码如下:

 

var asmName = new AssemblyName("Test");
            var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,
                                                                           AssemblyBuilderAccess.RunAndSave);

            var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.exe");

            var typeBldr = mdlBldr.DefineType("Hello", TypeAttributes.Public);

            var methodBldr = typeBldr.DefineMethod("SayHello", 
                MethodAttributes.Public | MethodAttributes.Static, 
                null,//return type
                null//parameter type
                );

            var il = methodBldr.GetILGenerator();//获取il生成器
            il.Emit(OpCodes.Ldstr,"Hello, World");
            il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{typeof(string)}));

            il.Emit(OpCodes.Call,  typeof(Console).GetMethod("ReadLine"));
            il.Emit(OpCodes.Pop);//读入的值会被推送至evaluation stack,而本方法是没有返回值的,因此,需要将栈上的值抛弃
            il.Emit(OpCodes.Ret);
           
            var t = typeBldr.CreateType();

            asmBuilder.SetEntryPoint(t.GetMethod("SayHello"));

            asmBuilder.Save("Main.exe");
 

   运行生成的Main.exe效果如下:

 

  

 

 

 

 

分享到:
评论

相关推荐

    Emit学习之旅

    学习Emit的简要教程,非常不错的说明!

    Emit 学习资源汇总

    Emit 学习资源汇总 Emit 学习资源汇总

    EMIT学习经典实例

    EMIT学习经典实例 EMIT学习经典实例 非常不错的一个实例

    Emit实现AOP示例

    纯手工打造Emit实现AOP private static void OverrideMethods(TypeBuilder tb, MethodInfo method) { if (!method.IsPublic|| !method.IsVirtual || IsObjectMethod(method)) return; Type[] paramTypes = ...

    Emit常用Opcode指令使用方法

    除了在网上查资料之外学习MSIL另一个好方法就是.Net Reflector和ildasm.exe配合使用,.Net Reflector可以把Emit代码转换为普通C#代码,ildasm.exe可以把普通C#代码转换为MSIL,不会写某一功能的Emit代码就先把它的C#...

    emit-response-size:将响应大小作为事件发送

    安装$ npm install emit-response-size用法const resSize = require ( 'emit-response-size' )const httpNdjson = require ( 'http-ndjson' )const stdout = require ( 'stdout-stream' )const http = require ( '...

    emit 类库 流畅api

    emit 类库 流畅api reflection

    Emit语法简单实现

    Emit代码示例,包括含参、不含参、静态、非静态等8个例子

    signal PYQT5

    emit@resource://devtools/shared/event-emitter.js:178:15 emit@resource://devtools/shared/event-emitter.js:255:5 setNodeFront@resource://devtools/client/framework/selection.js:153:5 onDetached@resource:...

    Emit创建动态类

    使用emit编写IL代码,在运行期创建实体类,不用在设计期创建实体。

    emit.js:JavaScript中的高效极简事件发射器

    emit.js JavaScript中的高效极简事件发射器。安装它可以与bower或npm一起使用: bower install emit.jsnpm install emit.js在HTML中包含emit.min.js ,并且在全局范围内现在可以使用emit对象: < script type =" ...

    Emit实现动态代理

    Emit实现动态代理 参照java的动态代理的使用感觉实现C#版

    $emit触发事件拿不到传递的参数.zip

    $emit触发事件拿不到传递的参数 https://blog.csdn.net/qq_36413371/article/details/102795742

    vue 组件之间事件触发($emit)与event Bus($on)的用法说明

    组件之间事件触发 之前使用组件,并不是很频繁,是水平...父子组件之间事件触发可以使用$emit $emit的使用方法如下: 在子组件中,写一个click点击事件。比如: cancelCU() { this.dialogVisible = false; this.$emi

    微型ORM核心 用Emit转换数据集到List

    现在很流行微型ORM框架,嘎嘎~~ 自己也写了一个,这是其中的实体转换代码的一部分 使用Emit 动态编译CIL 效率挺高的~~

    Emit实现从URL或者表单中创建对象

    Emit实现从URL或者表单中创建对象。 可通过前缀实现创建多对象。 性能高效,实用性高。

    primus-emit:在服务器和客户端之间发出事件

    primus-发射 primus-emit模块向 Primus 添加了client->server和server->client事件发射。安装该模块以primus-emit的名称发布: npm install --save primus-emit--save标志告诉npm自动添加包,并将其安装版本作为依赖...

    emit-cli:用于为所有语言和框架生成模板文件的CLI

    emit [filetype]... [filename]... 注意:此命令可以&将覆盖现有文件。 例如: 生成具有以下内容的文件Unicorn.html : emit html Unicorn 注意:默认名称是Index 为框架生成文件。 emit [filetype]... ...

    PyQT5 emit 和 connect的用法详解

    对于PyQT4, PyQT5在一些使用上有着比较明显的变化有很大的变化,让人惊讶是在emit和connect上的一些变化比较有意思,相信也是QT为了更好的和Python相结合做的改进。 先上一张图: 出现 AttributeError: ‘TCPWindow...

    emit-bindings:使用数据发射属性在 DOM 元素交互上发射事件

    要直接在页面中包含预构建版本,请使用 build/emit-(version).js 或 build/emit-(version).min.js: < script type =" text/javascript " src =" emit-1.1.2.min.js " > </ script > < script &gt...

Global site tag (gtag.js) - Google Analytics