当前位置: 首页>后端>正文

DispatchProxy in c#

概要

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 interface T.

创建一个实例,该实例实现了指定的接口,并且继承自指定类
意思也就是给你创建一个类型,实现了你想要的接口,然后使用的代理是你传入的代理

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;
    }
}

https://www.xamrdz.com/backend/3gs1940630.html

相关文章: