起因

最近想自己鼓捣个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);
}

第二步:完善类型信息

/// <summary>
/// 完善类型信息并生成
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Type BuildType<T>()
{
// 第一步得到的类型构建器
var typeBuilder = getTypeBuilder<T>();
// 获取类型的所有方法并遍历
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#通过反射动态生成类型继承接口并实现的更多相关文章

  1. 使用CodeDom动态生成类型

    .NET 3.5的时候加入了匿名类型这个特性,我们可以直接使用 new {name="abc"} 来直接生成一个对象.这个特性现在应用的地方很多,比如dapper的查询参数都是用匿 ...

  2. Roslyn 编译器Api妙用:动态生成类并实现接口

    在上一篇文章中有讲到使用反射手写IL代码动态生成类并实现接口. 反射的妙用:C#通过反射动态生成类型继承接口并实现 有位网友推荐使用 Roslyn 去脚本化动态生成,今天这篇文章就主要讲怎么使用 Ro ...

  3. c# 表达式目录树拷贝对象(根据对象类型动态生成表达式目录树)

    表达式目录树,在C#中用Expression标识,这里就不介绍表达式目录树是什么了,有兴趣可以自行百度搜索,网上资料还是很多的. 这里主要分享的是如何动态构建表达式目录树. 构建表达式目录树的代码挺简 ...

  4. [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...

  5. .Net 中的反射(动态创建类型实例) - Part.4

    动态创建对象 在前面节中,我们先了解了反射,然后利用反射查看了类型信息,并学习了如何创建自定义特性,并利用反射来遍历它.可以说,前面三节,我们学习的都是反射是什么,在接下来的章节中,我们将学习反射可以 ...

  6. Java下的框架编程(反射,泛型,元数据,CGLib,代码动态生成,AOP,动态语言嵌入)

    Java 虽然没有动态语言般暴起,但仍然天连天,水接水的生出好多框架技术---反射(reflection),泛型(generics),元数据(annotation),proxies(proxy/cgl ...

  7. .Net 中的反射(动态创建类型实例)

    动态创建对象 在前面节中,我们先了解了反射,然后利用反射查看了类型信息,并学习了如何创建自定义特性,并利用反射来遍历它.可以说,前面三节,我们学习的都是反射是什么,在接下来的章节中,我们将学习反射可以 ...

  8. <经验杂谈>C#中一种最简单、最基本的反射(Reflection):通过反射获取方法函数

    说起反射之前和很多用C#/.net的同仁们一样,相比于一般应用层对数据的增删改查总有点觉得深奥到难以理解.其实程序这东西,用过.实践过就很简单,我一直这么认为. 先说下概念:反射 Reflection ...

  9. java反射基础知识(五)反射应用实践

    详解Java反射各种应用   Java除了给我们提供在编译期得到类的各种信息之外,还通过反射让我们可以在运行期间得到类的各种信息.通过反射获取类的信息,得到类的信息之后,就可以获取以下相关内容: Cl ...

随机推荐

  1. 常见JS

    1.获取当前月份第一天 var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; v ...

  2. 单页应用后退不刷新方案(vue & react)

    引言 前进刷新,后退不刷新,是一个类似app页面的特点,要在单页web应用中做后退不刷新,却并非一件易事. 为什么麻烦 spa的渲染原理(以vue为例):url的更改触发onHashChange/pu ...

  3. java 从零开始手写 RPC (04) -序列化

    序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...

  4. IO之字节流

    什么是字节流 像操作 图片 视频 mp4 文档(里面可能有图片) 等等 注意点 必须使用try catch finally 来包 不用throws(流是要关闭的 如果中途抛错 throws 无法将流关 ...

  5. iframe、SameSite与CEF

    iframe.SameSite与CEF 背景 本人使用CEF(或是Chrome)来加载开发的前端页面,其中使用iframe嵌入了第三方页面,在第三方页面中需要发送cookie到后端,然而加载会报错,第 ...

  6. HPE ProLiant 系列服务器Microsoft Windows 2008 R2系统下网卡绑定方法

    HPE Network Configuration Utility(以下简称NCU) 网卡绑定工具,用户可以通过该工具很方便的把服务器的多个网卡捆绑到一起以达到容错和增加可用带宽的目的. 1.打开NC ...

  7. DistSQL:像数据库一样使用 Apache ShardingSphere

    Apache ShardingSphere 5.0.0-beta 深度解析的第一篇文章和大家一起重温了 ShardingSphere 的内核原理,并详细阐述了此版本在内核层面,特别是 SQL 能力方面 ...

  8. 七牛云的 python sdk 是如何 批量删除资源的

    今天做项目的时候用到七牛云,关于对资源的操作是在后端做的,用的SDK,但是,在网上没找到详细的解析,官方文档也没有太详细的解说,所以无奈只好看下源码 这里做一下简单的记录 from qiniu imp ...

  9. 【HMS Core 6.0全球上线】华为钥匙环服务,打造跨应用跨形态无缝登录体验

    华为钥匙环服务(Keyring),是HMS Core在安全领域开放的全新服务,为全球开发者提供用户认证凭据(以下简称"凭据")本地存储和跨应用.跨形态共享能力,帮助您在安卓应用.快 ...

  10. python os.walk处理树状目录结构的文件

    在项目工作中,时常需要用到处理文件的方法,尤其是在windows环境下的树状目录结构 os.walk恰好能完美的处理这种树状目录结构文件,能高效地帮助我们得到我们需要处理的文件 目录结构: Deskt ...