AI智能
改变未来

反射的妙用:C#通过反射动态生成类型继承接口并实现

###起因最近想自己鼓捣个

RPC

,想着简化

RPC

调用方式,直接申明接口,然后根据接口的属性去配置

RPC

调用的相关信息。有一种说法叫声明式调用。简单来说就是,声明一个

interface

,动态继承并实例化,然后打点调用。

今天这边篇章讲的就是前半部分:动态继承并实例化。

###相关知识点反射、IL(中间语言)

###框架背景asp.net core

###主要思路通过反射,去动态生成

class

,并继承和实现

interface

###相关属性说明

AssemblyBuilder

:表示动态程序集

ModuleBuilder

:表示动态程序集内的动态模块

TypeBuilder

:表示动态类型

MethodBuilder

:表示动态方法

ILGenerator

:IL代码生成器

上述几点是这边文章中会用到的一些对象。

###开干#####第一步:得到类型构建器

/// <summary>/// 生成动态类型/// </summary>/// <typeparam name="T"></typeparam>/// <param name="assemblyName">程序集名称</param>/// <returns></returns>private static TypeBuilder getTypeBuilder<T>(){// T类型所属的程序集名称AssemblyName assName = typeof(T).Assembly.GetName();// 动态程序集(Run表示该程序集只运行不保存)AssemblyBuilder assyBuilder = AssemblyBuilder.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);// 在程序集中创建动态模块,模块名自定义ModuleBuilder modBuilder = assyBuilder.DefineDynamicModule("MyMod");// 动态类名String newTypeName = "User";// 动态类的属性,Class和PublicTypeAttributes newTypeAttribute = TypeAttributes.Class | TypeAttributes.Public;// 动态类型的父类,这里不需要所以为nullType newTypeParent = null;// 动态类实现需要实现的接口Type[] newTypeInterfaces = new Type[] { typeof(T) };// 得到动态类型构建器return modBuilder.DefineType(newTypeName, newTypeAttribute, newTypeParent, newTypeInterfaces);}

###第二步:完善类型信息////// 完善类型信息并生成/////////public static Type BuildType(){// 第一步得到的类型构建器var typeBuilder = getTypeBuilder();// 获取类型的所有方法并遍历MethodInfo[] targetMethods = typeof(T).GetMethods();foreach (MethodInfo targetMethod in targetMethods){// 只针对Public方法if (targetMethod.IsPublic){// 得到方法的各个参数的类型ParameterInfo[] paramInfo = targetMethod.GetParameters();// 方法的参数类型Type[] paramType = new Type[paramInfo.Length];for (int i = 0; i < paramInfo.Length; i++){paramType[i] = paramInfo[i].ParameterType;}// 传入方法签名,得到方法构建器(方法名、方法属性、返回参数类型、方法参数类型)MethodBuilder methodBuilder = typeBuilder.DefineMethod(targetMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, targetMethod.ReturnType, paramType);

// 要生成具体类,方法的实现是必不可少的,而方法的实现是通过Emit IL代码来产生的// 得到IL生成器ILGenerator ilGen = methodBuilder.GetILGenerator();// 定义一个字符串(为了判断方法是否被调用)ilGen.Emit(OpCodes.Ldstr, "我被调用了");// 调用WriteLine函数ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));// 定义object类型的局部变量LocalBuilder local = ilGen.DeclareLocal(typeof(object));// 将索引为 0 的局部变量加载到栈的最顶层ilGen.Emit(OpCodes.Ldloc_0, local);// 判断是否需要返回值if (methodBuilder.ReturnType == typeof(void)){ilGen.Emit(OpCodes.Pop);}else{// 判断返回类型是否是值类型if (methodBuilder.ReturnType.IsValueType){ilGen.Emit(OpCodes.Unbox_Any, methodBuilder.ReturnType);}else{// 强制转换变量为指定类型(返回值 类型)ilGen.Emit(OpCodes.Castclass, methodBuilder.ReturnType);}}// 返回ilGen.Emit(OpCodes.Ret);}}return typeBuilder.CreateType();}

###第三步:注入前两步已经将动态生成类型并继承接口的过程描述完成了,我们现在将生成的动态类型注入到框架并使用。

// 先准备一个接口public interface IUserService{string getname();}// 自定义注入中间件public static IServiceCollection AddEmit<T>(this IServiceCollection service){// 生成的动态类型var type = DynamicImplementation.BuildType<T>();// 继承的接口var itype = typeof(T);// 注入service.AddScoped(itype, type);return service;}// startup文件// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services.AddEmit<IUserService>();}

###第四步:调用

private readonly IUserService _userService;public HomeController(IUserService userService){_userService = userService;}[HttpGet]public IActionResult Get(){_userService.getname();return Ok();}

就这样,动态生成类型并实现接口的操作就完成了。文章中涉及到的:

OpCodes

大家或许不太理解相关的意思,要理解需要对

IL

代码有一定的了解,大家可以自行去

msdn

进行了解。

如果动态实现的方法比较复杂,不知道怎么编写相关

IL

代码,教大家一种便捷的方式。

有一个工具叫

ILDASM

,可以查看相关代码对应的

IL(中间语言)

代码。

###在 vs 中集成 ILDASM打开 工具 外部工具 添加

ILDASM

工具在安装

vs

后就存在,我的地址(也就是命令)是:

C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v10.0A\\bin\\NETFX 4.8 Tools\\x64\\ildasm.exe

配置完毕后点击应用,工具选项中就会出现

ILDASM

选项

下面就是

ILDASM

工具的界面信息,以及具体的代码对照,大家先把需要动态生成的方法编写完成后通过

ILDASM

工具查看代码的接口再对照去编写动态生成的代码。

今天这篇文章就到这里了,下面我也要去继续完善相关的代码了,如果完成效果还行我也会继续分享出来。

无绪分享

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 反射的妙用:C#通过反射动态生成类型继承接口并实现