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 和接口调用的方法,不同的方法实际可以从多个类里面调用
我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新
如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
dotnet 动态代理魔法书的更多相关文章
- Emit动态代理.NetCore迁移之旅
[前言] 前面我们介绍了Aop 从静态代理到动态代理:https://www.cnblogs.com/7tiny/p/9657451.html 我们在.NetFramework平台下使用微软提供的Em ...
- 技术的正宗与野路子 c#, AOP动态代理实现动态权限控制(一) 探索基于.NET下实现一句话木马之asmx篇 asp.net core 系列 9 环境(Development、Staging 、Production)
黄衫女子的武功似乎与周芷若乃是一路,飘忽灵动,变幻无方,但举手抬足之间却是正而不邪,如说周芷若形似鬼魅,那黄衫女子便是态拟神仙. 这段描写出自<倚天屠龙记>第三十八回. “九阴神抓”本是& ...
- netcore 之动态代理(微服务专题)
动态代理配合rpc技术调用远程服务,不用关注细节的实现,让程序就像在本地调用以用. 因此动态代理在微服务系统中是不可或缺的一个技术.网上看到大部分案例都是通过反射自己实现,且相当复杂.编写和调试相当不 ...
- .NET 下基于动态代理的 AOP 框架实现揭秘
.NET 下基于动态代理的 AOP 框架实现揭秘 Intro 之前基于 Roslyn 实现了一个简单的条件解析引擎,想了解的可以看这篇文章 https://www.cnblogs.com/weihan ...
- JDK动态代理
一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...
- AOP之Castle DynamicProxy 动态代理
这里主要介绍使用castle这个动态代理,在.net一些开源的框架里可以找到它的影子,就连微软的rchard也是使用这个进行方法拦截等可以基于这个进行方法拦截,在这个方面PostSharp算是比较好用 ...
- java动态代理的2种实现方式
java的动态代理在接java的api上有说明,这里就不写了.我理解的代理: 对特定接口中特定方法的功能进行扩展,这就是代理.代理是通过代理实例关联的调用处理程序对象调用方法. 下面通过一个例子看一下 ...
- JDK动态代理实现原理
之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的.直到看了他的文章才彻底明白,附网址:htt ...
- java中动态代理的实现
动态代理的实现 使用的模式:代理模式. 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.类似租房的中介. 两种动态代理: (1)jdk动态代理,jdk动态代理是由Java内部的反射机制 ...
随机推荐
- oracle怎么捕获用户登录信息,如SID,IP地址等
可以利用登录触发器,如 CREATE OR REPLACE TRIGGER tr_login_record AFTER logon ON DATABASE DECLARE miUserSid NUMB ...
- Person Re-identification 系列论文笔记(七):PCB+RPP
Beyond Part Models: Person Retrieval with Refined Part Pooling Sun Y, Zheng L, Yang Y, et al. Beyond ...
- zend studio打开文件提示unsupported character encoding
zend studio打开文件提示unsupported character encoding,是文件的编码方式错误. 有可能是PHP代码中,charset={CHARSET} ,用了变量的形式调用编 ...
- UDP传输对象--JavaSE考试题
Server.java package com.sxt.udp.object; import java.io.ByteArrayInputStream; import java.io.ObjectIn ...
- ELK练习
1.ELK练习 PUT s3/_doc/ { "mappings" : { "doc" : { "properties" : { " ...
- 类的重载case2
/*功能:类的成员属性和成员方法*/ public class Person{ public static void main(String args[]){ TruePerson ...
- 【Objective-C】-空指针和野指针
一.什么是空指针和野指针 1.空指针 1> 没有存储任何内存地址的指针就称为空指针(NULL指针) 2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0. 下面两个都是空指针 ...
- SDUT-2119_数据结构实验之链表四:有序链表的归并
数据结构实验之链表四:有序链表的归并 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 分别输入两个有序的整数序列(分别包 ...
- Python 基础 --初识Python
python的起源 python是一门 解释型弱类型编程语言. 特点: 简单.明确.优雅 python的解释器 CPython. 官方提供的. 内部使用c语言来实现 PyPy. 一次性把我们的代码解释 ...
- K8s中Pod健康检查源代码分析
了解k8s中的Liveness和Readiness Liveness: 表明是否容器正在运行.如果liveness探测为fail,则kubelet会kill掉容器,并且会触发restart设置的策略. ...