官网地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.ilgenerator.emit?view=netcore-3.1
一、认识 Emit
使用Emit方法可以以编写C#代码的方式生成IL代码并执行 。
二、简单使用
Example:创建程序集、创建模块、创建类、创建构造函数、创建方法并调用
static void Main(string[] args) { // -- 创建程序集 AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Example.Generator"), AssemblyBuilderAccess.RunAndCollect); // -- 创建模块 ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Generators"); // -- 创建 Generator 类 TypeBuilder typeBuilder = moduleBuilder.DefineType("Generator"); // -- 创建构造函数 ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); // - 填充构造函数内需要执行的IL代码 ILGenerator constructGenerator = constructorBuilder.GetILGenerator(); // - 定义一个字符串 constructGenerator.Emit(OpCodes.Ldstr, "通过Emit构建构造函数中的逻辑代码"); // - 调用Console.WriteLine()输出这句话 constructGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); // - 返回 constructGenerator.Emit(OpCodes.Ret); // -- 创建一个方法 MethodBuilder method = typeBuilder.DefineMethod("Start", MethodAttributes.Public, typeof(void), new Type[] { typeof(string) }); // - 声明一个方法参数,名称为 name method.DefineParameter(0, ParameterAttributes.None, "name"); // - 编写方法内部的IL代码 ILGenerator methodGenerator = method.GetILGenerator(); // - 将传入的参数 name 打印出来 methodGenerator.Emit(OpCodes.Ldarg_1); methodGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); methodGenerator.Emit(OpCodes.Ret); // -- 创建实例 Type instance = typeBuilder.CreateType(); object proxy = Activator.CreateInstance(instance);
// -- 调用 MethodInfo methodInfo = proxy.GetType().GetMethod("Start"); methodInfo.Invoke(proxy, new string[] { "Hello Elson" }); Console.ReadKey(); }
三、重点
constructGenerator.Emit(OpCodes.Ldstr, "通过Emit构建构造函数中的逻辑代码"); constructGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); constructGenerator.Emit(OpCodes.Ret);
OpCodes.Ldstr:Pushes a new object reference to a string literal stored in the metadata.
可以理解为:string a = "Content"
OpCodes.Call:Calls the method indicated by the passed method descriptor.
可以理解为:以描述的形式调用方法。
我们原先调用方法的方式是:Console.WriteLine("Content");而使用 Emit 调用方法需要描述该方法:类类型,方法名称,参数类型。
OpCodes.Ret:Returns from the current method, pushing a return value (if present) from the callee's evaluation stack onto the caller's evaluation stack.
可以理解为:return value
// -- 定义一个方法:方法名称,访问级别,方法特性,返回值类型,参数类型
MethodBuilder method = typeBuilder.DefineMethod("Start", MethodAttributes.Public, typeof(void), new Type[] { typeof(string) }); // -- 声明一个方法参数:参数索引,参数特性,参数名称 method.DefineParameter(0, ParameterAttributes.None, "name"); ILGenerator methodGenerator = method.GetILGenerator(); methodGenerator.Emit(OpCodes.Ldarg_1); methodGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); methodGenerator.Emit(OpCodes.Ret);
OpCodes.Ldarg_1:Loads the argument at index 1 onto the evaluation stack.
获取参数索引位1上的参数,如果不是静态方法,OpCodes.Ldarg_0 获取的是 this,当前对象实例
class Example { public void GetContent(string content) { //Codes.Ldarg_0 = Example var _this = this; //Codes.Ldarg_1 = content var param = content } }
四、结语
欢迎大家积极指正,共同学习进步!
谦良恭卑,信诚实至;
生活不易,共勉同求。