C# 动态代理实现方法 C#如何使用DispatchProxy创建动态代理

1次阅读

dispatchproxy 是 .net core 2.1+ 和 .net 5+ 内置的仅支持接口代理的轻量级动态代理基类,需继承并重写 invoke 方法,通过 create 创建代理实例,且必须手动注入目标对象,不支持类代理、aot 编译及 .net framework。

C# 动态代理实现方法 C#如何使用DispatchProxy创建动态代理

DispatchProxy 是什么,能不能直接用

DispatchProxy 是 .NET Core 2.1+ 和 .NET 5+ 内置的轻量级动态代理基类,用于在运行时生成代理类型,拦截方法调用。它不依赖第三方库(如 Castle.Core),也不需要 System.Reflection.Emit 的复杂操作,但**仅支持接口代理**——不能代理类、不能代理无接口的类型。

常见误用:试图让 DispatchProxy 代理一个普通类(比如 class UserService),结果抛出 NotSupportedException: Cannot proxy a non-Interface type。这是设计限制,不是 bug

如何正确创建并使用 DispatchProxy 代理

必须定义接口,并让代理类继承 DispatchProxy,重写 Invoke 方法。代理实例通过 DispatchProxy.Create<t>()</t> 创建,其中 T 必须是接口类型。

  • 代理类需继承 DispatchProxy,且声明为 public(否则 Create 会失败)
  • Invoke 方法里可访问 target(即被代理的真实实现)、method(被调用的方法信息)、args参数数组
  • 若未手动调用 target?.GetType().GetMethod(...).Invoke(...),方法不会真正执行——DispatchProxy 不自动转发,必须显式处理

示例:

public interface ICalculator { int Add(int a, int b); } public class LoggingProxy : DispatchProxy {     private ICalculator _target;     protected override object Invoke(MethodInfo targetMethod, object[] args)     {         Console.WriteLine($"Calling {targetMethod.Name} with {string.Join(", ", args)}");         return targetMethod.Invoke(_target, args); // 必须手动转发     } } // 使用: var proxy = DispatchProxy.Create<ICalculator, LoggingProxy>(); // 注意:proxy 此时还不是可用对象,需设置内部 target var realImpl = new CalculatorImpl(); typeof(LoggingProxy).GetField("_target", BindingFlags.NonPublic | BindingFlags.Instance)     .SetValue(proxy, realImpl);

为什么代理对象调用后没日志、也没报错

最常见原因:没给代理类的私有字段(如 _target)赋值,或字段名/访问权限不对。因为 DispatchProxy.Create 只生成代理实例,不初始化其内部状态;它也不提供构造函数注入机制。

  • 字段名必须完全匹配(大小写敏感),且要用 BindingFlags.NonPublic | BindingFlags.Instance
  • 如果代理类用了 private readonly 字段,反射无法赋值,得改成 private
  • .NET 6+ 中若启用了 AOT 编译(如发布为 native AOT),DispatchProxy 会被禁用,运行时报 PlatformNotSupportedException
  • 泛型接口(如 IRepository<t></t>)可以代理,但 Create<irepository>, Proxy>()</irepository> 中的 User 必须是具体类型,不能是泛型参数

DispatchProxy 的性能和替代方案对比

DispatchProxy 的调用开销比直接调用高约 3–5 倍(取决于方法复杂度),主要来自反射调用 MethodInfo.Invoke 和装箱。它适合低频、调试、AOP 日志等场景,不适合高频核心路径(如游戏循环、高频数学计算)。

  • 想避免反射调用?可改用 Expression.Lambda 或源生成器(Source Generator)预生成代理,但开发成本显著上升
  • 需要代理类而非接口?只能换方案:Castle.Core(需 NuGet)、DynamicProxy(同属 Castle)、或 .NET 8 的 System.Runtime.CompilerServices.CallInterceptors(实验性,仅限特定平台)
  • 注意:.NET Framework 不支持 DispatchProxy,它只存在于 System.Reflection.DispatchProxy 命名空间中,且最低要求 .NET Core 2.1

真正麻烦的不是怎么写代理,而是怎么安全地把真实实现塞进代理实例里——字段名拼错、访问权限漏设、AOT 环境下静默失效,这些才是线上出问题的高发点。

text=ZqhQzanResources