- Notifications
You must be signed in to change notification settings - Fork 22
System.Reflection.Emit
L edited this page May 18, 2018
·
2 revisions
小学弟做的一个demo,动态生成类,觉得蛮有意思的,存个档
相关资料:
https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/emitting-dynamic-methods-and-assemblies https://github.com/FanHuaRan/dotnetbeancopier usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Reflection;usingSystem.Reflection.Emit;usingSystem.Text;usingSystem.Threading.Tasks;namespaceDemo2{publicinterfaceISayHello{StringSayHello(Stringname);}classProgram{privatestaticreadonlyStringSAVE_DOMAIN_NAME="MyDynamic.dll";staticvoidMain(string[]args){// 获取当前的应用程序域varappDomain=AppDomain.CurrentDomain;// 动态在当前的AppDomain当中创建一个程序集 varassemblyName=newAssemblyName("MyDynamicAssembly");// 该程序集的访问模式设置为run,意味着该程序集只驻留在内存,不会持久化// var assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);// 该程序集的访问模式设置为runAndSave,意味着该程序集既可驻留内存,又可以持久化varassemblyBuilder=appDomain.DefineDynamicAssembly(assemblyName,AssemblyBuilderAccess.RunAndSave);// 动态在程序集内创建一个模块// var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyDynamicModule");// 动态在程序集内创建一个模块,RunAndSave模式下,后面需要指定保存的文件名varmoduleBuilder=assemblyBuilder.DefineDynamicModule("MyDynamicModule",SAVE_DOMAIN_NAME);// 动态在模块内创建一个类vartypeBuilder=moduleBuilder.DefineType("MyDynamicType",TypeAttributes.Public);// 该类实现ISayHello接口 ,实现接口需要遵守可访问性的原则typeBuilder.AddInterfaceImplementation(typeof(ISayHello));// 定义一个方法,该方法实际上就是实现接口的SayHello方法varreturnType=typeof(String);Type[]paramTypes={typeof(String)};varmethodBuilder=typeBuilder.DefineMethod("SayHello",MethodAttributes.Public|MethodAttributes.Virtual,CallingConventions.Standard,returnType,paramTypes);// 使用ILGenerator为方法进行实现varilGenerator=methodBuilder.GetILGenerator();/** 全部需要使用IL中间代码来编写 **/// 如果修改操作码,则填补空间ilGenerator.Emit(OpCodes.Nop);// 将索引为1的参数加到栈顶ilGenerator.Emit(OpCodes.Ldstr,"Hello {0}");// 将索引为1的加到栈顶ilGenerator.Emit(OpCodes.Ldarg_1);//调用Console.WriteLine方法,参数就是当前栈顶的两个参数ilGenerator.Emit(OpCodes.Call,(typeof(Console)).GetMethod("WriteLine",newType[]{typeof(string),typeof(Object)}));// 如果修改操作码,则填补空间ilGenerator.Emit(OpCodes.Nop);// 定义一个字符串常量到栈顶ilGenerator.Emit(OpCodes.Ldstr,"Hello {0}");// 将索引为1的参数加到栈顶ilGenerator.Emit(OpCodes.Ldarg_1);//// 调用String.Format方法,参数就是当前栈顶的两个参数,结果会放在栈顶ilGenerator.Emit(OpCodes.Call,typeof(string).GetMethod("Format",newType[]{typeof(string),typeof(string)}));
#region 似乎是ILGenerator的BUG,这段代码和cs文件生成的IL代码一样,但总是运行时错误,这段代码会把.maxstack参数多加1个,实际上不应该的
//// 栈顶存储着拼接结果,将其弹出`// ilGenerator.Emit(OpCodes.Stloc_0);//// 定义一个label(指令流中的一条指令位置)//Label targetInstruction = ilGenerator.DefineLabel();//// 无条件地将控制转移到targetInstruction//ilGenerator.Emit(OpCodes.Br_S, targetInstruction);//// targetInstruction标记为当前流所在位置//ilGenerator.MarkLabel(targetInstruction);//// 将索引为0的变量加到栈顶//ilGenerator.Emit(OpCodes.Ldloc_0);
#endregion
// 方法返回,如果需要返回值,就把栈顶的参数作为返回值返回ilGenerator.Emit(OpCodes.Ret);// 建立方法对接口的重载关系(这儿最好说成是实现)MethodInfosayHelloInfo=typeof(ISayHello).GetMethod("SayHello");typeBuilder.DefineMethodOverride(methodBuilder,sayHelloInfo);// 正式创建类vardynamicType=typeBuilder.CreateType();// RunAndSave模式下可保存该程序集,方便调试assemblyBuilder.Save(SAVE_DOMAIN_NAME);// 反射创建对象vardynamicObj=Activator.CreateInstance(dynamicType);// 通过接口来使用该对象ISayHellosayHello=dynamicObjasISayHello;varresult=sayHello.SayHello(".NET");//上面生成的代码等同于/* public class MySayHello : ISayHello { public String SayHello(string name) { Console.WriteLine("Hello {0}", name); return "Hello " + name; } } */Console.ReadLine();}}}