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

来源:互联网 发布:淘宝 图片裁剪 不清楚 编辑:程序博客网 时间:2024/06/05 03:47

原文地址: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的基本方法。这样,我们就基本可以解决遇到的大部分问题了。欢迎大家指正。

原创粉丝点击