概要
DispatchProxy Class (System.Reflection) | Microsoft Learn
Provides a mechanism for instantiating proxy objects and handling their method dispatch.
官方文档的介绍还是很简单的,这是一个抽象的类,需要手动写个类继承一下才可以使用,需要注意的也就两个方法
Create<T,TProxy>() | Creates an object instance that derives from class
TProxy
and implements interfaceT
.
创建一个实例,该实例实现了指定的接口,并且继承自指定类
意思也就是给你创建一个类型,实现了你想要的接口,然后使用的代理是你传入的代理
Invoke(MethodInfo, Object[]) | Whenever any method on the generated proxy type is called, this method is invoked to dispatch control.
这个方法是Abstract的,也就是需要重写,用来处理调用的方法。重写完成之后,这个TProxy类作为Create的泛型参数,所有通过这种方式创建的类,在调用方法时都会调用Invoke方法,这也就是Dispatch的意思。这里之所以会调用Invoke,其实是Create的时候做的一些操作,确保创建的新类能满足这个特性
看起来简单,但是一旦涉及到啥代理啊,分发啊,就很容易乱作一团。
简单使用
先来看一下最基本的用法
public interface ILogger
{
void LogMessage(string message);
}
public class LoggingProxy : DispatchProxy
{
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
Console.WriteLine($"invoke method TargetMethod : {targetMethod}");
return null;
}
}
class Program
{
static void Main()
{
var loggingProxy = DispatchProxy.Create<ILogger, LoggingProxy>();
loggingProxy.LogMessage("123");
//out put :invoke method TargetMethod : Void LogMessage(System.String)
var st = loggingProxy.ToString();
//no out put
Console.Read();
}
}
代码如上图所示,定义了一个接口ILogger
,表示记录日志的接口
定义了一个类LoggingProxy
,继承自DispatchProxy
,然后重写了Invoke()
方法,里面就记录了一下调用的方法,啥都不干。
主方法里面,调用了一下Create()
方法,创建了一个ILogger
的实例,然后调用了一下该实例的LogMessage()
方法,可以看到果然调用到Invoke()
了。
手贱又试了一下ToString()
方法,结果没有调用Invoke()
。按照官方的说法,Whenever any method on the generated proxy type is called, this method is invoked to dispatch control.看来官方说法还不是很准确啊,但是这样也符合我们的预期,毕竟ToString
是继承自Object
,不是我们接口给出的功能。
还试了一下 DispatchProxy.Create<ILogger, DispatchProxy>(),运行时报错,因为DispatchProxy
是抽象类。哈哈,确实,这么干也没啥意义,因为光用DispatchProxy
没有实现相当于啥信息都没说
好,回到正题上来,我们已经使用了DispatchProxy
,也确实像官方说的一样,但是这个有什么实际效果呢?
上面的例子可能在测试里面有用,我需要测试一个功能,输入的参数里面需要一个接口,但是我嫌麻烦,不想实现这个接口再传个实例进来,这个时候传个Proxy进来,想测试的方法返回值是啥Invoke()
就返回啥,这样其实也行,但是测试有专门的框架,功能要比这个完善多了。
正常用法
上面我们的Invoke()
方法里面有两个参数,一个是方法的信息,一个是参数的信息,那如果我有一个接口的实现,这里是不是可以调用这个实现的方法呢,这样的话能做的事情就很多了,但是方法调用总归需要一个实例的,我们在Proxy里面塞个实例。代码搞起
public interface ILogger
{
void LogMessage(string message);
}
public class Logger : ILogger
{
public void LogMessage(string message)
{
Console.WriteLine($"Logging: {message}");
}
}
接口还是上面的接口,我们定义了一个类Logger
,实现了这个接口
public class LoggingProxy : DispatchProxy
{
private ILogger _logger;
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
_logger.LogMessage($"Entering {targetMethod.Name}");
object result = null;
try
{
// Call the original method
result = targetMethod.Invoke(_logger, args);
_logger.LogMessage($"Exiting {targetMethod.Name}");
}
catch (Exception ex)
{
_logger.LogMessage($"Exception in {targetMethod.Name}: {ex.Message}");
throw;
}
return result;
}
public static T Create<T>(ILogger logger)
{
object proxy = Create<T, LoggingProxy>();
((LoggingProxy)proxy)._logger = logger;
return (T)proxy;
}
}
我们还是定义了LoggingProxy
,继承自DispatchProxy
。看一下里面的内容,我们在这个类里面定义了一个属性_logger
,这个对象作为Invoke()
的实例,只要把它赋值了,就可以调用它的方法了,在Invoke()
里面,我调用了_logger
的这个方法。但是这个_logger
怎么赋值呢?我们创建了一个静态方法Create()
,传入ILogger
的实例,返回包装了ILogger
的Proxy,这样就随用随取了。
这里面也有一个很有意思的东西,看一下Create()
方法里面: proxy
是继承自LoggingProxy的子类,而_logger
属性是LoggingProxy
的private属性,为什么可以将proxy转换成LoggingProxy
并访问它的_logger
属性呢?在这里,我们从子类里面访问到了父类的private属性,private不是不能继承的吗?
实际确实很少这么用,毕竟一般都是在类的外面访问属性,这样就访问不到private属性。但是在这里可以直接访问proxy
的_logger
属性,因为还是在父类的实现内部,可以访问自己的Private属性
搞个简单的代码试试
class Person
{
private int age;
public static Person Create(Baby s)
{
s.age= 0;
return s;
}
}
class Baby:Person
{
}
class Program
{
static void Main(string[] args)
{
Baby s = new Baby();
var p= Person.Create(s);
Console.WriteLine();
}
}
上面这个例子可能更简单一点,这段代码是可以运行的,所以其实private 属性不一定对于子类完全不可见的,private只是限定了作用域,实际来说子类还是继承了父类的private属性的(意思是子类其实还是有这个属性,不然按理说哪怕转换回父类也访问不到这个属性),只是在子类的作用域内不可见,所以像没继承一样
回来,我们继续看一下Main()
方法里面的调用
class Program
{
static void Main()
{
// Create an instance of the original Logger
ILogger originalLogger = new Logger();
// Create a logging proxy for the ILogger interface
ILogger loggingProxy = LoggingProxy.Create<ILogger>(originalLogger);
// Use the logging proxy as if it were the original Logger
loggingProxy.LogMessage("Hello, DispatchProxy!");
/*
Output:
Logging: Entering LogMessage
Logging: Hello, DispatchProxy!
Logging: Exiting LogMessage
*/
}
}
这里面实例化了一个Logger
对象,然后调用我们自己写的Create()
方法,获得了一个loggingProxy
,后面调用了一下LogMessage()
方法,可以看到我们的代理其实起作用了,方法前后都打印了日志。
那么这个有什么用呢,上面的例子里面,我们只是打印了一下方法入口和出口,实际上我们可以做很多东西,比如根据业务需要更改返回值,比如调用方法的时候实现一些额外功能。
这个其实就是代理的含义:某个对象委托了一个代理,这个代理不改变原先对象的结构,但是通过代理我可以实现一些额外的更改或者功能。举个例子,我需要去打官司,我可以去学习那些弯弯绕绕的法律,怎么诉讼之类的(对象自己实现额外的功能)。我也可以委托一下律师帮我处理,这样我什么都不用学,只需要出庭(委托一个代理帮我做,对象自己不需要更改)。
使用场景
- 日志记录 Logging (打印方法调用的时间,参数,返回值等等)
- 缓存 Caching (可以在代理里面创建缓存,减少重复计算)
- 校验 Validation (在方法调用之前验证参数)
- 事务管理 Transaction Management
- 安全性校验 Security (核对用户权限等等)
- 分布式系统 Distributed Systems
可以处理网络问题或者分布式引起的问题(多个方法都可以都用一个代理)
-- 网络不通重试若干次
-- 防止同时多次调用相同远程方法,等到远程方法返回了才能再次调用(通过在代理上创建bool属性)
-- 压力派发(轮流访问不同的远程服务器,或者更加服务器忙碌状态动态分配)
-- 异常捕获 服务器发生异常怎么处理 - 动态代理 Dynamic Proxies
一些框架允许动态代理,需要动态创建类型
和装饰器模式对比
可以发现,其实这个玩意目的和装饰器模式是一样的,都是在不改变目标结构的基础上拓展功能。区别只是在于实现方式不同,但是正式实现方式不同导致了他们在使用时存在一定差异
- 装饰器模式是在编译时创建的类,都需要手动指定,而DispatchProxy是在运行的时候动态创建的类
- 装饰器模式不是强制需要定义一个公共的接口的(正常操作是定义一个公共接口),定义成目标类的子类也可以实现类似的效果。而DispatchProxy的接口是必须的,没有接口没有办法create了
- 装饰器模式相对来说更加模块化,需要什么接口定义什么,多个装饰器之间也可以相互组合。DispatchProxy相对来说可以定义的更加宽泛,这样很多对象都可以使用,可以在一起集中处理,为多个对象创建相同的代理(参考下面的代码)但是多个代理之间不太好组合
public class LoggingInterceptor : DispatchProxy
{
private object _target;
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
Console.WriteLine($"Method {targetMethod.Name} called with arguments {string.Join(", ", args)}");
var result = targetMethod.Invoke(_target, args);
Console.WriteLine($"Method {targetMethod.Name} returned {result}");
return result;
}
public static T Create<T>(T target)
{
object proxy = Create<T, LoggingInterceptor>();
((LoggingInterceptor)proxy)._target = target;
return (T)proxy;
}
}