Emit创建常见元素—自定义类型

转载 2012年03月27日 09:36:35

原文地址:http://www.cnblogs.com/MythYsJh/archive/2010/04/28/1723398.html

本文讲述用Emit创建枚举,结构体,类,接口等类型.

  同样,让我们先搭好大体框架:

            AssemblyName assemName = new AssemblyName();
            assemName.Name 
= "EmitStudy3";

            AssemblyBuilder asmBuilder 
=
               AppDomain.CurrentDomain.DefineDynamicAssembly(assemName,
                  AssemblyBuilderAccess.RunAndSave);

            var mdlBldr 
= asmBuilder.DefineDynamicModule("EmitStudy3""EmitStudy3.dll");

            
//TODO:添加自定义类型

            asmBuilder.Save(
"EmitStudy3.dll");

 

一、创建枚举

  枚举通过ModuleBuilder.DefineEnum来实现。

       var enumBldr = mdlBldr.DefineEnum("Sex", TypeAttributes.Public, typeof(int));

 

  这里定义了一个基础类型为int,名称为Sex的public枚举。下面,让我们再为该枚举添加'内容':

            enumBldr.DefineLiteral("Male"0);
            enumBldr.DefineLiteral(
"Female"1);

 

  之后也需要像创建类一样使用

            enumBldr.CreateType();

 

来完成一个枚举的创建。这是用Reflector查看就可以看到如下一个枚举了:

public enum Sex
{
    Male,
    Female
}

 

 

 

  二、结构体(struct)

  在ModuleBuilder中并不存在DefineStruct之类的方法。那该如何创建一个结构体呢?我们首先用C#创建一个结构体然后反编译成IL就会发现结构体实质是继承于ValueType的一个类。因此我们可以这样来创建一个结构体:

            var nameStruct = mdlBldr.DefineType("Name", TypeAttributes.Public, typeof(ValueType));
            var fldFirstName 
= nameStruct.DefineField("FirstName"typeof(string), FieldAttributes.Private);
            var fldLastName 
= nameStruct.DefineField("LastName"typeof(string), FieldAttributes.Private);

            var methodGetFirstName 
= nameStruct.DefineMethod("GetFirstName", MethodAttributes.Public, CallingConventions.Standard, typeof(string), null);
            var methodSetFirstName 
= nameStruct.DefineMethod("SetFirstName", MethodAttributes.Public, CallingConventions.Standard, null,new []{ typeof(string)});

            var ilGetFirstName 
= methodGetFirstName.GetILGenerator();
            ilGetFirstName.Emit(OpCodes.Ldarg_0);
            ilGetFirstName.Emit(OpCodes.Ldfld, fldFirstName);
            ilGetFirstName.Emit(OpCodes.Ret);

            var ilSetFirstName 
= methodSetFirstName.GetILGenerator();
            ilSetFirstName.Emit(OpCodes.Ldarg_0);
            ilSetFirstName.Emit(OpCodes.Ldarg_1);
            ilSetFirstName.Emit(OpCodes.Stfld, fldFirstName);
            ilSetFirstName.Emit(OpCodes.Ret);

            var prptFirstName 
= nameStruct.DefineProperty("FirstName", PropertyAttributes.None, typeof(string), null);
            prptFirstName.SetGetMethod(methodGetFirstName);
            prptFirstName.SetSetMethod(methodSetFirstName);

            var methodGetLastName 
= nameStruct.DefineMethod("GetLastName", MethodAttributes.Public, CallingConventions.Standard, typeof(string), null);
            var methodSetLastName 
= nameStruct.DefineMethod("SetLastName", MethodAttributes.Public, CallingConventions.Standard, nullnew[] { typeof(string) });

            var ilGetLastName 
= methodGetLastName.GetILGenerator();
            ilGetLastName.Emit(OpCodes.Ldarg_0);
            ilGetLastName.Emit(OpCodes.Ldfld, fldLastName);
            ilGetLastName.Emit(OpCodes.Ret);

            var ilSetLastName 
= methodSetLastName.GetILGenerator();
            ilSetLastName.Emit(OpCodes.Ldarg_0);
            ilSetLastName.Emit(OpCodes.Ldarg_1);
            ilSetLastName.Emit(OpCodes.Stfld, fldLastName);
            ilSetLastName.Emit(OpCodes.Ret);

            var prptLastName 
= nameStruct.DefineProperty("LastName", PropertyAttributes.None, typeof(string), null);
            prptLastName.SetGetMethod(methodGetLastName);
            prptLastName.SetSetMethod(methodSetLastName);

            nameStruct.CreateType();

 

  上面就创建了一个包含FirstName和LastName的结构体Name。

  

  三、创建接口

  同样在ModuleBuilder中也没有DefineInterface这样的方法。而通过观察IL代码同样可以发现接口其实也是类。因此我们还是可以想创建类一样来创建接口.

            var iStudentBldr = mdlBldr.DefineType("IStudent", TypeAttributes.Public| TypeAttributes.Interface | TypeAttributes.Abstract, null);
            iStudentBldr.DefineMethod(
"Update", MethodAttributes.Virtual | MethodAttributes.Abstract, nullnull);
            iStudentBldr.CreateType();

 

这样就创建了一个仅包含一个Update方法的接口IStudent。在创建接口时也要注意TypeAttributes需声明为 TypeAttributes.Interface | TypeAttributes.Abstract,而接口包含的方法的MethodAttributes则必须为MethodAttributes.Virtual | MethodAttributes.Abstract

  

  四、创建类

  已经有很多这方面的例子,这里就不再赘述。

 

  五、委托和事件

  老规矩,从反编译的IL代码入手。可以发现我们创建的委托其实是派生自MultiDelegate类的类。而MultiDelegate这个类比较特殊,它不能从C#代码里被继承,但在IL代码却可以。因此可以如下来定义委托。

            var delPropertyChangedHandler = mdlBldr.DefineType("PropertyChanedHandler", TypeAttributes.Public, typeof(MulticastDelegate));
            delPropertyChangedHandler.CreateType();

 

  但是这样的定义似乎总觉得很奇怪,因为委托还包含返回值及参数等信息,这些在这段代码都没有得到体现。那么该如何创建委托呢?让我们还是从现有的入手,以EventHandler为例,在Reflector下它的结构为

     

      事实上,创建委托必须包含一个构造函数和Invoke方法。Invoke方法的参数则是委托的参数,返回类型即为委托的返回类型。

            var delPropertyChangedHandler = mdlBldr.DefineType("PropertyChanedHandler", TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate));
            var constructor 
= delPropertyChangedHandler.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
            constructor.SetImplementationFlags(MethodImplAttributes.Runtime 
| MethodImplAttributes.Managed);

            var methodInvoke 
= delPropertyChangedHandler.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual, nullnew[] { typeof(string) });
            methodInvoke.SetImplementationFlags(MethodImplAttributes.Managed 
| MethodImplAttributes.Runtime);

            var delType = delPropertyChangedHandler.CreateType();

 

构造函数和Invoke方法都使用了SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime),因为二者的方法体是在运行时才能决定的。

       既然已经创建了委托,让我们再用此委托添加一个事件。定义事件就相对容易些了,直接使用TypeBuilder.DefineEvent即可.

          var typeBldr = mdlBldr.DefineType("Sdudent", TypeAttributes.Public);
          
//其余部分
          typeBldr.DefineEvent("OnNameChanged", EventAttributes.None, delType);
          typeBldr.CreateType();

 

 

六、小结

      我们已经可以用Emit创建常见元素了。当然,Emit还可以创建很多其他内容,不可能介绍完全,关键是要掌握通过C#和IL对比来完成Emit的基本方法。这样,我们就基本可以解决遇到的大部分问题了。欢迎大家指正。

Emit学习之旅(2):创建常见元素—基础部分

上回已经用Emit创建了一个简单的类型。下面接着说说如何为动态创建的类添加实例成员,属性,方法等。 准备工作这次来创建一个Student类。首先准备好大致框架:...
  • nic7968
  • nic7968
  • 2011年09月08日 17:43
  • 354

如何在“元素为自定义类型的vector”中查找指定的元素?

我采用的方法是利用STL中提供的find方法,关键是在定义类型中重载“==”操作符 代码示例: / vecfind.cpp : 定义控制台应用程序的入口点。 // #include "st...
  • tigernana
  • tigernana
  • 2012年02月18日 11:27
  • 3308

C# Emit 技术(1)

C# Emit 技术(1) 动态生成一个类对于AOP,O/R Mapping等技术非常有帮助。对于Java来说,问题不大,而对于.NET,则要麻烦些(主要麻烦在于实现代码的生成需要IL),故猜测...
  • lijing_zhaisky
  • lijing_zhaisky
  • 2012年04月07日 10:37
  • 5563

Emit学习-基础篇-为动态类添加属性、构造函数、方法

Emit学习-基础篇-为动态类添加属性、构造函数、方法 我们通过一个计算A+B的动态类来演示如何为一个动态类添加属性、构造函数、方法,以及在方法中使用类中定义的属性,按照惯例,我们先给出要实...
  • wl58796351
  • wl58796351
  • 2013年03月24日 18:41
  • 1114

javascript中组合使用构造函数模式和原型模式创建对象

首先来讲一下构造函数创建对象。ECMAScript中的构造函数可用来创建特定类型的对象。请看下面示例了解一下构造函数模式:function Person(name, age, job){ th...
  • zhongguohaoshaonian
  • zhongguohaoshaonian
  • 2016年09月12日 11:26
  • 1807

sql创建自定义数据类型

 除了使用系统提供的数据类型外,用户还可以根据需要用自定义的数据类型来定义表的列或声明变量。  7.3.1 用Enterprise Manager 创建用户自定义数据类型  用Enterprise M...
  • fenghu89
  • fenghu89
  • 2007年11月29日 20:37
  • 2633

oracle自定义类型

 最近整理里常见数据库的数据类型,发现o racle有一个自定义字段类型。简单的研究了一下:   1、定义一个类型 Sql代码  CREATE OR REPLACE TYPE Pro...
  • a275838263
  • a275838263
  • 2016年04月05日 10:22
  • 929

postgresql定义字段类型

如果建表,含有字段名为value的字段,建表能够成功,但是无法对该做除查询以外的其他操作,无论是插入还是修改字段名,都会报column "value"does not exist的错误...
  • guangshen8
  • guangshen8
  • 2017年09月05日 11:03
  • 93

$emit与v-on

V-on绑定事件,每个vue实例都实现了事件接口:使用$on(eventName)监听事件、 $emit(eventName)触发事件。必须在模板里直接用v-on绑定来实现 在这个例子中,可以看...
  • fighting_sunnyGirl
  • fighting_sunnyGirl
  • 2017年06月14日 10:09
  • 574

vue中 关于$emit的用法

vue中$emit的用法
  • l284969634
  • l284969634
  • 2017年11月21日 18:02
  • 2539
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Emit创建常见元素—自定义类型
举报原因:
原因补充:

(最多只允许输入30个字)