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

C#中如何将事件回调转换为可等待的async异步任务

最优实现方案

你需要的效果可以通过.NET 内置的TaskCompletionSource<TResult>实现,这是将回调/事件驱动逻辑封装为可等待 Task(TAP 模式)的标准方案,不需要引入额外依赖。


核心封装代码

public static Task<TEvent> WaitForEventAsync<TEvent>(object subscriber, Action triggerAction)
{
    // 配置 RunContinuationsAsynchronously 避免阻塞事件触发线程
    var tcs = new TaskCompletionSource<TEvent>(TaskCreationOptions.RunContinuationsAsynchronously);

    // 定义临时事件处理器
    void EventHandler(TEvent eventArgs)
    {
        // 收到事件后第一时间取消订阅,避免内存泄漏
        EventSystem.Unsubscribe<TEvent>(subscriber, EventHandler);
        // 完成 Task,传递事件参数
        tcs.TrySetResult(eventArgs);
    }

    // 先订阅事件再执行触发逻辑,避免事件提前触发丢失
    EventSystem.Subscribe<TEvent>(subscriber, EventHandler);

    try
    {
        triggerAction();
    }
    catch (Exception ex)
    {
        // 触发操作报错时同步取消订阅,把异常透传到 Task 中
        EventSystem.Unsubscribe<TEvent>(subscriber, EventHandler);
        tcs.TrySetException(ex);
    }

    return tcs.Task;
}

调用方式

和你期望的写法完全一致:

EventFoo e = await WaitForEventAsync<EventFoo>(this, DoSomethingThatWillTriggerAnEvent);

如果封装为扩展方法,调用会更简洁:

// 扩展方法定义
public static Task<TEvent> WaitForEventAsync<TEvent>(this object subscriber, Action triggerAction)
{
    // 内部逻辑和上面的实现完全一致
}

// 调用示例
EventFoo e = await this.WaitForEventAsync<EventFoo>(DoSomethingThatWillTriggerAnEvent);

注意事项

  • 必须在事件触发、超时、触发逻辑报错的所有分支中取消事件订阅,避免 EventSystem 持有实例引用导致内存泄漏
  • 如果需要超时控制,可以结合Task.WhenAny实现,示例:
public static async Task<TEvent> WaitForEventAsync<TEvent>(object subscriber, Action triggerAction, TimeSpan timeout)
{
    var tcs = new TaskCompletionSource<TEvent>(TaskCreationOptions.RunContinuationsAsynchronously);
    void EventHandler(TEvent eventArgs)
    {
        EventSystem.Unsubscribe<TEvent>(subscriber, EventHandler);
        tcs.TrySetResult(eventArgs);
    }
    EventSystem.Subscribe<TEvent>(subscriber, EventHandler);

    try
    {
        triggerAction();
        var finishedTask = await Task.WhenAny(tcs.Task, Task.Delay(timeout));
        if (finishedTask != tcs.Task)
        {
            EventSystem.Unsubscribe<TEvent>(subscriber, EventHandler);
            throw new TimeoutException("等待事件触发超时");
        }
        return await tcs.Task;
    }
    catch (Exception ex)
    {
        EventSystem.Unsubscribe<TEvent>(subscriber, EventHandler);
        tcs.TrySetException(ex);
        throw;
    }
}
  • 如果需要接收多次触发的事件,可以将返回值改为IAsyncEnumerable<TEvent>实现事件流消费。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 09:39:03