C#使用Emit生成构造函数和属性
假设我们需要一个名叫Kitty的类,其在Pets程序集下。
1 // specify a new assembly name 2 var assemblyName = new AssemblyName("Pets"); 3 4 // create assembly builder 5 var assemblyBuilder = AppDomain.CurrentDomain 6 .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); 7 8 // create module builder 9 var moduleBuilder = assemblyBuilder.DefineDynamicModule("PetsModule", "Pets.dll"); 10 11 // create type builder for a class 12 var typeBuilder = moduleBuilder.DefineType("Kitty", TypeAttributes.Public);
定义字段
Kitty类包含两个私有字段_id和_name。用类型构造器来定义,
1 var fieldId = typeBuilder.DefineField( 2 "_id", typeof(int), FieldAttributes.Private); 3 var fieldName = typeBuilder.DefineField( 4 "_name", typeof(string), FieldAttributes.Private);
定义构造函数
Kitty类包含一个有两个参数的构造函数,参数一为整型id,参数而为字符串型name。构造函数内,将参数id赋值给私有字段_id,将参数name赋值给私有字段_name,
1 Type objType = Type.GetType("System.Object"); 2 ConstructorInfo objCtor = objType.GetConstructor(new Type[0]); 3 4 Type[] constructorArgs = { typeof(int), typeof(string) }; 5 6 var constructorBuilder = typeBuilder.DefineConstructor( 7 MethodAttributes.Public, CallingConventions.Standard, constructorArgs); 8 ILGenerator ilOfCtor = constructorBuilder.GetILGenerator(); 9 10 ilOfCtor.Emit(OpCodes.Ldarg_0); 11 ilOfCtor.Emit(OpCodes.Call, objCtor); 12 ilOfCtor.Emit(OpCodes.Ldarg_0); 13 ilOfCtor.Emit(OpCodes.Ldarg_1); 14 ilOfCtor.Emit(OpCodes.Stfld, fieldId); 15 ilOfCtor.Emit(OpCodes.Ldarg_0); 16 ilOfCtor.Emit(OpCodes.Ldarg_2); 17 ilOfCtor.Emit(OpCodes.Stfld, fieldName); 18 ilOfCtor.Emit(OpCodes.Ret);
定义属性
为Kitty类创建Id和Name两个属性,读取和设置私有字段_id和_name。C#中的属性定义的getter和setter分别为两个方法。
1 var methodGetId = typeBuilder.DefineMethod( 2 "GetId", MethodAttributes.Public, typeof(int), null); 3 var methodSetId = typeBuilder.DefineMethod( 4 "SetId", MethodAttributes.Public, null, new Type[] { typeof(int) }); 5 6 var ilOfGetId = methodGetId.GetILGenerator(); 7 ilOfGetId.Emit(OpCodes.Ldarg_0); // this 8 ilOfGetId.Emit(OpCodes.Ldfld, fieldId); 9 ilOfGetId.Emit(OpCodes.Ret); 10 11 var ilOfSetId = methodSetId.GetILGenerator(); 12 ilOfSetId.Emit(OpCodes.Ldarg_0); // this 13 ilOfSetId.Emit(OpCodes.Ldarg_1); // the first one in arguments list 14 ilOfSetId.Emit(OpCodes.Stfld, fieldId); 15 ilOfSetId.Emit(OpCodes.Ret); 16 17 // create Id property 18 var propertyId = typeBuilder.DefineProperty( 19 "Id", PropertyAttributes.None, typeof(int), null); 20 propertyId.SetGetMethod(methodGetId); 21 propertyId.SetSetMethod(methodSetId); 22 23 var methodGetName = typeBuilder.DefineMethod( 24 "GetName", MethodAttributes.Public, typeof(string), null); 25 var methodSetName = typeBuilder.DefineMethod( 26 "SetName", MethodAttributes.Public, null, new Type[] { typeof(string) }); 27 28 var ilOfGetName = methodGetName.GetILGenerator(); 29 ilOfGetName.Emit(OpCodes.Ldarg_0); // this 30 ilOfGetName.Emit(OpCodes.Ldfld, fieldName); 31 ilOfGetName.Emit(OpCodes.Ret); 32 33 var ilOfSetName = methodSetName.GetILGenerator(); 34 ilOfSetName.Emit(OpCodes.Ldarg_0); // this 35 ilOfSetName.Emit(OpCodes.Ldarg_1); // the first one in arguments list 36 ilOfSetName.Emit(OpCodes.Stfld, fieldName); 37 ilOfSetName.Emit(OpCodes.Ret); 38 39 // create Name property 40 var propertyName = typeBuilder.DefineProperty( 41 "Name", PropertyAttributes.None, typeof(string), null); 42 propertyName.SetGetMethod(methodGetName); 43 propertyName.SetSetMethod(methodSetName);
定义方法
为Kitty类增加一个ToString()方法,返回一个字符串值。
1 // create ToString() method 2 var methodToString = typeBuilder.DefineMethod( 3 "ToString", 4 MethodAttributes.Virtual | MethodAttributes.Public, 5 typeof(string), 6 null); 7 8 var ilOfToString = methodToString.GetILGenerator(); 9 var local = ilOfToString.DeclareLocal(typeof(string)); // create a local variable 10 ilOfToString.Emit(OpCodes.Ldstr, "Id:[{0}], Name:[{1}]"); 11 ilOfToString.Emit(OpCodes.Ldarg_0); // this 12 ilOfToString.Emit(OpCodes.Ldfld, fieldId); 13 ilOfToString.Emit(OpCodes.Box, typeof(int)); // boxing the value type to object 14 ilOfToString.Emit(OpCodes.Ldarg_0); // this 15 ilOfToString.Emit(OpCodes.Ldfld, fieldName); 16 ilOfToString.Emit(OpCodes.Call, 17 typeof(string).GetMethod("Format", 18 new Type[] { typeof(string), typeof(object), typeof(object) })); 19 ilOfToString.Emit(OpCodes.Stloc, local); // set local variable 20 ilOfToString.Emit(OpCodes.Ldloc, local); // load local variable to stack 21 ilOfToString.Emit(OpCodes.Ret);
保存类型
生成类型,并保存程序集至Pets.dll文件。
1 // then create the whole class type 2 var classType = typeBuilder.CreateType(); 3 4 // save assembly 5 assemblyBuilder.Save("Pets.dll");
反编译类
使用反编译器来查看生成的类,
1 using System; 2 public class Kitty 3 { 4 private int _id; 5 private string _name; 6 public int Id 7 { 8 get 9 { 10 return this._id; 11 } 12 set 13 { 14 this._id = value; 15 } 16 } 17 public string Name 18 { 19 get 20 { 21 return this._name; 22 } 23 set 24 { 25 this._name = value; 26 } 27 } 28 public Kitty(int id, string name) 29 { 30 this._id = id; 31 this._name = name; 32 } 33 public override string ToString() 34 { 35 return string.Format("Id:[{0}], Name:[{1}]", this._id, this._name); 36 } 37 }
完整代码
1 using System; 2 using System.Reflection; 3 using System.Reflection.Emit; 4 5 namespace EmitCreateMembers 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 // specify a new assembly name 12 var assemblyName = new AssemblyName("Pets"); 13 14 // create assembly builder 15 var assemblyBuilder = AppDomain.CurrentDomain 16 .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); 17 18 // create module builder 19 var moduleBuilder = assemblyBuilder.DefineDynamicModule("PetsModule", "Pets.dll"); 20 21 // create type builder for a class 22 var typeBuilder = moduleBuilder.DefineType("Kitty", TypeAttributes.Public); 23 24 // then create whole class structure 25 CreateKittyClassStructure(typeBuilder); 26 27 // then create the whole class type 28 var classType = typeBuilder.CreateType(); 29 30 // save assembly 31 assemblyBuilder.Save("Pets.dll"); 32 33 Console.WriteLine("Hi, Dennis, a Pets assembly has been generated for you."); 34 Console.ReadLine(); 35 } 36 37 private static void CreateKittyClassStructure(TypeBuilder typeBuilder) 38 { 39 // ---- define fields ---- 40 41 var fieldId = typeBuilder.DefineField( 42 "_id", typeof(int), FieldAttributes.Private); 43 var fieldName = typeBuilder.DefineField( 44 "_name", typeof(string), FieldAttributes.Private); 45 46 // ---- define costructors ---- 47 48 Type objType = Type.GetType("System.Object"); 49 ConstructorInfo objCtor = objType.GetConstructor(new Type[0]); 50 51 Type[] constructorArgs = { typeof(int), typeof(string) }; 52 53 var constructorBuilder = typeBuilder.DefineConstructor( 54 MethodAttributes.Public, CallingConventions.Standard, constructorArgs); 55 ILGenerator ilOfCtor = constructorBuilder.GetILGenerator(); 56 57 ilOfCtor.Emit(OpCodes.Ldarg_0); 58 ilOfCtor.Emit(OpCodes.Call, objCtor); 59 ilOfCtor.Emit(OpCodes.Ldarg_0); 60 ilOfCtor.Emit(OpCodes.Ldarg_1); 61 ilOfCtor.Emit(OpCodes.Stfld, fieldId); 62 ilOfCtor.Emit(OpCodes.Ldarg_0); 63 ilOfCtor.Emit(OpCodes.Ldarg_2); 64 ilOfCtor.Emit(OpCodes.Stfld, fieldName); 65 ilOfCtor.Emit(OpCodes.Ret); 66 67 // ---- define properties ---- 68 69 var methodGetId = typeBuilder.DefineMethod( 70 "GetId", MethodAttributes.Public, typeof(int), null); 71 var methodSetId = typeBuilder.DefineMethod( 72 "SetId", MethodAttributes.Public, null, new Type[] { typeof(int) }); 73 74 var ilOfGetId = methodGetId.GetILGenerator(); 75 ilOfGetId.Emit(OpCodes.Ldarg_0); // this 76 ilOfGetId.Emit(OpCodes.Ldfld, fieldId); 77 ilOfGetId.Emit(OpCodes.Ret); 78 79 var ilOfSetId = methodSetId.GetILGenerator(); 80 ilOfSetId.Emit(OpCodes.Ldarg_0); // this 81 ilOfSetId.Emit(OpCodes.Ldarg_1); // the first one in arguments list 82 ilOfSetId.Emit(OpCodes.Stfld, fieldId); 83 ilOfSetId.Emit(OpCodes.Ret); 84 85 // create Id property 86 var propertyId = typeBuilder.DefineProperty( 87 "Id", PropertyAttributes.None, typeof(int), null); 88 propertyId.SetGetMethod(methodGetId); 89 propertyId.SetSetMethod(methodSetId); 90 91 var methodGetName = typeBuilder.DefineMethod( 92 "GetName", MethodAttributes.Public, typeof(string), null); 93 var methodSetName = typeBuilder.DefineMethod( 94 "SetName", MethodAttributes.Public, null, new Type[] { typeof(string) }); 95 96 var ilOfGetName = methodGetName.GetILGenerator(); 97 ilOfGetName.Emit(OpCodes.Ldarg_0); // this 98 ilOfGetName.Emit(OpCodes.Ldfld, fieldName); 99 ilOfGetName.Emit(OpCodes.Ret); 100 101 var ilOfSetName = methodSetName.GetILGenerator(); 102 ilOfSetName.Emit(OpCodes.Ldarg_0); // this 103 ilOfSetName.Emit(OpCodes.Ldarg_1); // the first one in arguments list 104 ilOfSetName.Emit(OpCodes.Stfld, fieldName); 105 ilOfSetName.Emit(OpCodes.Ret); 106 107 // create Name property 108 var propertyName = typeBuilder.DefineProperty( 109 "Name", PropertyAttributes.None, typeof(string), null); 110 propertyName.SetGetMethod(methodGetName); 111 propertyName.SetSetMethod(methodSetName); 112 113 // ---- define methods ---- 114 115 // create ToString() method 116 var methodToString = typeBuilder.DefineMethod( 117 "ToString", 118 MethodAttributes.Virtual | MethodAttributes.Public, 119 typeof(string), 120 null); 121 122 var ilOfToString = methodToString.GetILGenerator(); 123 var local = ilOfToString.DeclareLocal(typeof(string)); // create a local variable 124 ilOfToString.Emit(OpCodes.Ldstr, "Id:[{0}], Name:[{1}]"); 125 ilOfToString.Emit(OpCodes.Ldarg_0); // this 126 ilOfToString.Emit(OpCodes.Ldfld, fieldId); 127 ilOfToString.Emit(OpCodes.Box, typeof(int)); // boxing the value type to object 128 ilOfToString.Emit(OpCodes.Ldarg_0); // this 129 ilOfToString.Emit(OpCodes.Ldfld, fieldName); 130 ilOfToString.Emit(OpCodes.Call, 131 typeof(string).GetMethod("Format", 132 new Type[] { typeof(string), typeof(object), typeof(object) })); 133 ilOfToString.Emit(OpCodes.Stloc, local); // set local variable 134 ilOfToString.Emit(OpCodes.Ldloc, local); // load local variable to stack 135 ilOfToString.Emit(OpCodes.Ret); 136 } 137 } 138 }