反射的妙用: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和Public TypeAttributes newTypeAttribute = TypeAttributes.Class | TypeAttributes.Public; // 动态类型的父类,这里不需要所以为null Type 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工具查看代码的接口再对照去编写动态生成的代码。
今天这篇文章就到这里了,下面我也要去继续完善相关的代码了,如果完成效果还行我也会继续分享出来。
- C# 反射-通过继承接口实现创建对象并调用方法
- C# Roslyn 编译器Api妙用:动态生成类并实现接口
- c# 动态生成继承类并实现序列化特性
- [Unity&C#&接口]通过接口调用不同类型 物体 的继承了接口的组件
- 对比:通过实现Runnable接口和继承thread类来生成多线程
- C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能
- 用C#通过反射实现动态调用WebService 告别Web引用
- 改进C#代码之24:通过定义并实现接口替代继承
- 通过代理接口在内存中动态生成代理类源代码并编译实现的真正动态代理
- c#通过反射查找接口的实现 并创造实例
- C# 禁止修改已装箱了的值类型的字段值,但是可以通过接口的方式实现
- C# 通过反射来动态创建泛型类型
- cxf restful 接口的多参数的通过反射动态生成sql的设计
- 分享一段C#反射代码-[Type是反射的入口]--[查看类型信息]--[动态生成对象]
- C#通过IConvertible接口来实现自定义类型转换和计算
- 《Effective C#》读书笔记——条目22:通过定义并实现接口替代继承<使用C#表达设计>
- 用C#通过反射实现动态调用WebService 告别Web引用(转载)
- C# 自定义类型通过实现IFormattable接口,来输出指定的格式和语言文化的字符串(例:DateTime)
- c#通过反射查找接口的实现 并创造实例
- 通过反射中的 TypeBuilder 来动态实现 INotifyPropertyChanged 接口