原文地址:http://www.cnblogs.com/MythYsJh/archive/2010/04/28/1723398.html
本文讲述用Emit创建枚举,结构体,类,接口等类型.
同样,让我们先搭好大体框架:
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来实现。
这里定义了一个基础类型为int,名称为Sex的public枚举。下面,让我们再为该枚举添加'内容':
enumBldr.DefineLiteral( " Female " , 1 );
之后也需要像创建类一样使用
来完成一个枚举的创建。这是用Reflector查看就可以看到如下一个枚举了:
{
Male,
Female
}
二、结构体(struct)
在ModuleBuilder中并不存在DefineStruct之类的方法。那该如何创建一个结构体呢?我们首先用C#创建一个结构体然后反编译成IL就会发现结构体实质是继承于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, null , new [] { 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代码同样可以发现接口其实也是类。因此我们还是可以想创建类一样来创建接口.
iStudentBldr.DefineMethod( " Update " , MethodAttributes.Virtual | MethodAttributes.Abstract, null , null );
iStudentBldr.CreateType();
这样就创建了一个仅包含一个Update方法的接口IStudent。在创建接口时也要注意TypeAttributes需声明为 TypeAttributes.Interface | TypeAttributes.Abstract,而接口包含的方法的MethodAttributes则必须为MethodAttributes.Virtual | MethodAttributes.Abstract。
四、创建类
已经有很多这方面的例子,这里就不再赘述。
五、委托和事件
老规矩,从反编译的IL代码入手。可以发现我们创建的委托其实是派生自MultiDelegate类的类。而MultiDelegate这个类比较特殊,它不能从C#代码里被继承,但在IL代码却可以。因此可以如下来定义委托。
delPropertyChangedHandler.CreateType();
但是这样的定义似乎总觉得很奇怪,因为委托还包含返回值及参数等信息,这些在这段代码都没有得到体现。那么该如何创建委托呢?让我们还是从现有的入手,以EventHandler为例,在Reflector下它的结构为
事实上,创建委托必须包含一个构造函数和Invoke方法。Invoke方法的参数则是委托的参数,返回类型即为委托的返回类型。
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, null , new [] { typeof ( string ) });
methodInvoke.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime);
var delType = delPropertyChangedHandler.CreateType();
构造函数和Invoke方法都使用了SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime),因为二者的方法体是在运行时才能决定的。
既然已经创建了委托,让我们再用此委托添加一个事件。定义事件就相对容易些了,直接使用TypeBuilder.DefineEvent即可.
// 其余部分
typeBldr.DefineEvent( " OnNameChanged " , EventAttributes.None, delType);
typeBldr.CreateType();
六、小结
我们已经可以用Emit创建常见元素了。当然,Emit还可以创建很多其他内容,不可能介绍完全,关键是要掌握通过C#和IL对比来完成Emit的基本方法。这样,我们就基本可以解决遇到的大部分问题了。欢迎大家指正。