You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可正确等待反射获取的可等待类型的通用C#装饰器?

如何在C#中实现适配异步方法的通用装饰器?

我正在研究如何在C#中实现适配异步方法的通用装饰器。多数示例使用仅提供同步Invoke方法的DispatchProxy。除非.NET对此进行改进并添加异步支持(相关议题已关闭),否则我们只能采用与普通特定装饰器相同的调用方式。问题似乎在于,由于可等待类型是基于模式实现的,目前没有简洁的方式检测该场景并编写正确代码,希望熟悉反射深层原理的人士能提供思路。


特定场景装饰器示例

手动编写特定场景的装饰器很简单,示例如下:

interface IMyWork
{
  Task DoStuff();
}

class MyWork : IMyWork
{
  public async Task DoStuff() { await Something(); }
}

// 普通装饰器实现
class MyDecoratedWork : IMyWork
{
  private readonly MyWork _real;

  public async Task DoStuff()
  {
    using var scope = Decorate();
    await _real.DoStuff();
    // 释放操作与Decorate处于同一上下文 - AsyncLocal等状态会被恢复
  }

  private IDisposable Decorate() { /* 返回作用域装饰对象 */ }
}

通用DispatchProxy装饰器框架

如果需要大量装饰器,通用实现会更具吸引力,典型的DispatchProxy框架如下:

class DispatchProxyScopedDecorator<TDecorated> : DispatchProxy
{
  private TDecorated? _decorated;
  private IScopeProvider? _scopeProvider;

  private void SetParameters(TDecorated decorated, IScopeProvider scopeProvider)
  {
    _decorated = decorated;
    _scopeProvider = scopeProvider;
  }

  protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
  {
    // 核心逻辑待实现
  }

  public static TDecorated Create(TDecorated decorated, IScopeProvider scopeProvider)
  {
    object created = Create<TDecorated, DispatchProxyScopedDecorator<TDecorated>>()!;
    ((DispatchProxyScopedDecorator<TDecorated>)created).SetParameters(decorated, scopeProvider);

    return (TDecorated)created;
  }
}

同步场景的简单实现

同步方法的装饰逻辑很直接:

protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
  using var scope = _scopeProvider.Decorate();
  return targetMethod?.Invoke(_decorated, args);
}

异步场景的实现困境

但Invoke是同步方法,要正确装饰返回可等待类型的方法调用,必须用新的可等待类型拦截返回值。

根据文档,可等待类型的唯一要求是实现GetAwaiter方法(AsyncStateMachineAttribute等属性不一定适用):

var scope = _scopeProvider.Decorate();

var result = targetMethod.Invoke(_decorated, args);

if (result is null)
{
  return null;
}

// 注意:这段代码不完整,还需要查找程序集中的扩展方法!
var awaiterMethod = result.GetType().GetMethod("GetAwaiter");

如果awaiterMethod不为null,说明返回值是可等待类型。我理想中易维护的实现是将逻辑封装到异步方法,让编译器处理复杂的等待逻辑:

async Task InterceptAsync(??? awaitable, MyScope scope)
{
  try
  {
    await awaitable;
  }
  finally
  {
    scope.Dispose();
  }
}

if (awaiterMethod is not null)
{
  return InterceptAsync(result, scope);
}

但这段代码无法编译,原因很明显——可等待类型并非单一类型,除了已弃用的dynamic外,没有能让编译器接受的类型填入???位置。

像所有装饰器一样,这种方式存在明显缺点(性能损耗、返回类型可能变更),但这种实现方式是否可行?我是否遗漏了其他方法?我的第一反应是,如果原方法返回的不是Task,上述实现会违反预期的方法签名——但这是否意味着每种不同的可等待类型都需要单独实现?

期待您的想法。


内容的提问来源于stack exchange,提问作者naviwhack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 18:41:02