2019-8-31-dotnet-动态代理魔法书
| title | author | date | CreateTime | categories |
|---|---|---|---|---|
|
dotnet 动态代理魔法书
|
lindexi
|
2019-08-31 16:55:58 +0800
|
2019-06-02 16:55:10 +0800
|
dotnet
|
看到标题的小伙伴是不是想知道什么是魔法书,如果你需要写一段代码,这段代码是在做神奇的业务,只有你查询到了魔法书你才能找到这个对象,同时你还需要实现自己的接口,通过自己实现的接口调用才能用到有趣的方法
在 C# 里面是不能直接让两个有相同方法的但没有继承的接口直接转换,但是通过透明代理和反射可以做到在不同的程序集定义的两个接口,这两个接口有相同的方法,那么将可以用另一个程序集的接口调用到传入程序集里面的接口
在本文开始之前,期望的读者是了解透明代理的,通过透明代理可以让每个调用方法之前先调用透明代理的方法。在透明代理的方法可以知道用户调用的是哪个方法,同时传入的参数是什么
先封装一个类,在这个类里面放一些定义好的实例,在透明代理方法里面通过传入用户调用的方法,使用反射调用对应实例的方法,然后将方法返回值返回
每次用户可以通过一个 Guid 才能获取透明代理,因为用户获取的是透明代理,是无法直接拿到对应的实例里面的字段,同时需要用户传入自己定义的接口,不想让用户访问的内容,用户是很难猜到的
这样的写法其实就和 COM 一样,从文档找到 COM 的 GUID 然后自己定义一个接口获取
先看我定义的使用方法
// 在第一个程序集注入了 F1 代码
var guid = new Guid("{97C70651-EE85-4AED-9E2F-AD73AF34CF5D}");
DynamicProxy.Add(guid,new F1());
// 自己实现两个接口
var f2 = DynamicProxy.GetObject<IF2>(guid);
Console.WriteLine(f2.GetName());
var f3 = DynamicProxy.GetObject<IF3>(guid);
Console.WriteLine(f3.GetName());
期望的写法是调用 DynamicProxy.Add 在一个程序集,获取变量在另一个程序集
这里的 F1 和接口 IF2 IF3 都没有继承关系
interface IF3
{
string GetName();
} interface IF2
{
string GetName();
} class F1
{
public string GetName()
{
return "林德熙是逗比";
}
}
只要两个接口之间有定义相同的,那么这两个接口之间就可以相互转换,也就是在用户自己写的代码,是可以做到从文档里面找到其中需要使用的几个方法,然后定义自己的接口,通过上面方法就可以拿到
这个方法每次返回的对象都不相同,用户也不知道是不相同的对象,例如我可以将相同的一个对象作为两个不同的 Guid 传入,然后告诉用户两个不同的接口
var guid1 = new Guid("{97C70651-EE85-4AED-9E2F-AD73AF34CF5D}");
var guid2 = new Guid("{05D1936F-7121-43BA-B986-A42A56555AAE}");
var f1 = new F1();
DynamicProxy.Add(guid1, f1);
DynamicProxy.Add(guid2, f1);
// 自己实现两个接口
var f2 = DynamicProxy.GetObject<IF2>(guid1);
Console.WriteLine(f2.GetName());
Console.WriteLine(f2.GetName(new F3()));
var f3 = DynamicProxy.GetObject<IF3>(guid2);
Console.WriteLine(f3.GetName());
如果是在不同的业务上,估计小伙伴很难知道其实使用的 f2 和 f3 是相同一个类
这是我从 VisualStudio 学到的黑科技,通过这个方式定义,可以隐藏很多用户不需要了解的,同时可以随意的变动逻辑,只需要保持存在对应的接口就可以
例如现在我通过 F1 传入,但是我发现其中的某几个 Guid 获取的对应方法我需要修改了,于是我可以再创建一个 F2 的类,修改原因的 Guid 的值。还是使用上面的代码,我发现了通过 guid2 获取到的 IF3 的 GetName 方法需要修改。但是我不能影响到 IF2 的逻辑,于是我可以在传入的时候修改代码
- DynamicProxy.Add(guid2, f1);
+ DynamicProxy.Add(guid2, new F2());
这样就可以做到原有的 IF2 的逻辑不变,同时修改了另一个接口的方法
这里 DynamicProxy 的代码其实很简单,只是重写 Invoke 方法,从方法里面拿到用户调用的方法,通过反射调用实例的方法
class Express
{
/// <inheritdoc />
public Express(Lazy<object> instance)
{
_instance = instance;
} public object Instance => _instance.Value; private readonly Lazy<object> _instance;
} class Proxy : RealProxy
{
/// <inheritdoc />
public Proxy(Type classToProxy, Express express) : base(classToProxy)
{
Express = express;
} public Express Express { get; } /// <inheritdoc />
public override IMessage Invoke(IMessage msg)
{
MethodCallMessageWrapper callMessageWrapper = new MethodCallMessageWrapper((IMethodCallMessage) msg);
MethodInfo methodBase = callMessageWrapper.MethodBase as MethodInfo;
if (methodBase == null)
return null; var instance = Express.Instance;
var type = instance.GetType(); Type[] argumentTypeList;
if (callMessageWrapper.Args?.Any() is true)
{
argumentTypeList = callMessageWrapper.Args.Select(temp => temp.GetType()).ToArray();
}
else
{
argumentTypeList = Type.EmptyTypes;
} var method = type.GetMethod(methodBase.Name, argumentTypeList); if (method == null)
{
throw new ArgumentException("调用方法不匹配,找不到" + methodBase + "方法");
} return new ReturnMessage(
method.Invoke(instance, callMessageWrapper.Args),
callMessageWrapper.Args, callMessageWrapper.ArgCount, callMessageWrapper.LogicalCallContext,
callMessageWrapper);
}
}
创建 DynamicProxy 类,这个类就是给用户用的,当然实际的命名将会修改
class DynamicProxy
{
public static void Add(Guid guid, object instance)
{
_dictionary[guid] = new Express(new Lazy<object>(() => instance));
} public static T GetObject<T>(Guid guid)
{
if (!typeof(T).IsInterface)
{
throw new ArgumentException();
} return (T) new Proxy(typeof(T), _dictionary[guid]).GetTransparentProxy();
} // 实际代码请使用缓存池
private static Dictionary<Guid, Express> _dictionary = new Dictionary<Guid, Express>(); class Express
{
// 忽略代码
} class Proxy : RealProxy
{
// 忽略代码
} }
在实际使用上面代码还需要做很多更改,例如支持 Express 里面添加多个实例,也就是给用户一个 Guid 和接口调用的方法,不同的方法实际可以从多个类里面调用
2019-8-31-dotnet-动态代理魔法书的更多相关文章
- dotnet 动态代理魔法书
看到标题的小伙伴是不是想知道什么是魔法书,如果你需要写一段代码,这段代码是在做神奇的业务,只有你查询到了魔法书你才能找到这个对象,同时你还需要实现自己的接口,通过自己实现的接口调用才能用到有趣的方法 ...
- JDK动态代理深入理解分析并手写简易JDK动态代理(上)
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html 作者:夜月归途 出处:http://www.guitu ...
- 技术的正宗与野路子 c#, AOP动态代理实现动态权限控制(一) 探索基于.NET下实现一句话木马之asmx篇 asp.net core 系列 9 环境(Development、Staging 、Production)
黄衫女子的武功似乎与周芷若乃是一路,飘忽灵动,变幻无方,但举手抬足之间却是正而不邪,如说周芷若形似鬼魅,那黄衫女子便是态拟神仙. 这段描写出自<倚天屠龙记>第三十八回. “九阴神抓”本是& ...
- 大数据之路week05--day07(序列化、类加载器、反射、动态代理)
遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题 a,什么叫序列化和反序列化b,作用.为啥要实现这个 Serializable 接口,也就是为啥要序列化c,seri ...
- [z]Java代理(jdk静态代理、动态代理和cglib动态代理)
一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 ...
- Spring笔记(三)AOP前篇之动态代理
AOP思想是将程序中的业务代码与服务代码进行分离,在运行时进行结合.比较强调程序的层次结构,是一种面向切面的编程.而在AOP实现的底层主要用到了动态代理,而动态代理又分为JDK动态代理和CGLIB动态 ...
- cglib源码分析(四):cglib 动态代理原理分析
本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...
- 动态代理与AOP
1. 代理的分类: 静态代理:每个代理类只能为一个接口服务 动态代理:可以通过一个代理类完成全部的代理功能(由JVM生成实现一系列接口的代理类,即:生成实现接口的类的代理) 2. 动态代理: 在Jav ...
- 动态代理的两种实现方式(JDK/Cglib)
=========================================== 原文链接: 动态代理的两种实现方式(JDK/Cglib) 转载请注明出处! ================== ...
- Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程
spring-aop-4.3.7.RELEASE 在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...
随机推荐
- 记录--react native 封装人脸 检测、美颜组件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 本组件目前只能用在React Native 的iOS端 本组件来之实际中的开发需求:可以检测并且标记人脸,实现基本的美颜,可进行拍照.换行 ...
- .NET分布式Orleans - 2 - Grain的通信原理与定义
Grain 是 Orleans 框架中的基本单元,代表了应用程序中的一个实体或者一个计算单元. 每个Silo都是一个独立的进程,Silo负责加载.管理和执行Grain实例,并处理来自客户端的请求以及与 ...
- .NET开源免费、功能强大的 Windows 截图录屏神器
前言 今天大姚给大家分享一款.NET开源免费(基于GPL3.0开源协议).功能强大.简洁灵活的 Windows 截图.录屏.Gif动图制作神器:ShareX. 功能特性 ShareX 是一个开源的屏幕 ...
- 简单c++构建第一人称
本文内容为UE4.27的文档教程 GameMode确定 新建的项目会自动生成GameMode,如果有更改,而不是使用默认的GameMode类,就需要在引擎的设置中更改 角色的实现 前后左右移动 //前 ...
- Web、Android等程序开发中src引入外部文件和资源的方法总结
方法一:使用相对于当前文件(源文件)的相对路径 使用 ../ 对于这个例子来说 ../ 把路径带到了项目根目录的下一级目录 1 <script src="../static/js/wo ...
- 实用 Linux 命令 Windos 命令 实例演示 持续更新中
实用 Linux 命令 Windos 命令 实例演示 持续更新中 目录 实用 Linux 命令 Windos 命令 实例演示 持续更新中 Linux 命令 [Command [options] [lo ...
- 16 JavaScript逗号运算符
16 JavaScript逗号运算符 Python 逗号运算符一般用于组合多个表达式,其返回值是最后一个表达式的值,例如: function s(){ console.log(1), console. ...
- AT-abc347(C,D)
AtCoder Beginner Contest 347 C - Ideal Holidays 这场做得最头疼的题 分析 容易想到先用$(d_i+a+b-1)%(a+b)+1$把$d_i$映射到$[1 ...
- van-cell如何使用插槽
van-cell 是 Vant 组件库中的一个单元格组件,用于展示列表中的信息.Vant 是一个轻量.可靠的移动端 Vue 组件库. 在 Vant 中,van-cell 组件提供了多个插槽(slot) ...
- 【直播回顾】参与ArkUI,共建OpenHarmony繁荣生态
5月31日晚上19点,战"码"先锋第三期直播 <参与ArkUI,共建OpenHarmony繁荣生态> ,在OpenHarmony社群内成功举行. 本期课程,由华为终 ...