起因

最近想自己鼓捣个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. AT3945-[ARC092D]Two Faced Edges【dfs】

    正题 题目链接:https://www.luogu.com.cn/problem/AT3945 题目大意 \(n\)个点\(m\)条边的一张图,对于每条边求它翻转后强连通分量数量是否变化. \(1\l ...

  2. hdu5909-Tree Cutting【FWT】

    正题 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5909 题目大意 给出\(n\)和\(m\)(\(m=2^k\)).再给出一个大小为\(n\)的树 ...

  3. C++ostringstream用法

    ostringstream用法 1.类型转换 要求包含头文件; 字符串和int之间的互相转换: int num = 100; string str = ""; std::ostri ...

  4. Linux下iptables学习笔记

    Linux下iptables学习笔记 在Centos7版本之后,防火墙应用已经由从前的iptables转变为firewall这款应用了.但是,当今绝大多数的Linux版本(特别是企业中)还是使用的6. ...

  5. 从零入门 Serverless | 课时5 函数的调试与部署

    作者 | 江昱 阿里巴巴高级产品经理 本文整理自<Serverless 技术公开课>,关注"Serverless"公众号,回复"入门",即可获取 S ...

  6. 一站式交付体验:云效+Kubernetes

    背景 云效依托于阿里巴巴研发效能多年规模化持续交付,赋能云上开发者专为云端用户提供的一站式研发协作平台.Kubernetes,由Google开源的容器集群管理平台,面向运维侧提供自动化的集群和应用管理 ...

  7. javascript-原生-结构

    1.获取用户输入内容的方法 window.prompt("提示信息","默认值"); 获取用户输入内容(字符串类型),返回用户输入内容. 2.顺序结构:所有语句 ...

  8. SharkCTF2021 Babyhttp && get_or_lose

    两道web. Babyhttp: 直接dirsearch,发现同时存在git和bak泄露:经验证,git的没用. 访问index.php.bak, 下载源码: 抓包,改包,发包即可. get_or_l ...

  9. 【Spring】IoC容器 - 依赖查找

    前言 上一篇文章已经学习了[IoC的主要实现策略]有2种: 1.依赖查找 2.依赖注入 这里稍加详细的介绍一下依赖查找 1.依赖查找的方式 依赖查找的方式可以以多种维度来划分: 1.按名称/类型/注解 ...

  10. 92.反转链表II

    题目 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right .请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 . ...