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

如何基于返回Task的ReadAsync正确重写Stream.BeginRead方法

关于自定义Stream复用ReadAsync实现BeginRead的解答

复用可行性

完全可以复用ReadAsync的代码实现BeginRead,这种方案合法且高效,不需要维护同步异步两套独立逻辑,能大幅降低编码和后续维护成本。

实现原理

BeginRead属于.NET传统APM异步编程模型接口,要求返回IAsyncResult类型;而ReadAsync是TAP基于任务的异步模式实现,返回Task<int>类型。.NET的Task本身已经实现了IAsyncResult接口,只需要做简单的适配即可满足APM的接口要求。

通用实现代码

BeginRead实现

public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
    // 创建携带自定义状态的TaskCompletionSource
    var tcs = new TaskCompletionSource<int>(state);
    // 调用你已经实现的ReadAsync逻辑
    ReadAsync(buffer, offset, count, CancellationToken.None).ContinueWith(task =>
    {
        // 同步ReadAsync的执行结果
        if (task.IsFaulted)
        {
            tcs.TrySetException(task.Exception.InnerExceptions);
        }
        else if (task.IsCanceled)
        {
            tcs.TrySetCanceled();
        }
        else
        {
            tcs.TrySetResult(task.Result);
        }
        // 触发APM模式要求的回调
        callback?.Invoke(tcs.Task);
    }, TaskContinuationOptions.ExecuteSynchronously);
    return tcs.Task;
}

对应EndRead实现

public override int EndRead(IAsyncResult asyncResult)
{
    if (asyncResult is not Task<int> readTask)
    {
        throw new ArgumentException("传入的IAsyncResult不是当前Stream生成的有效实例", nameof(asyncResult));
    }
    // 正确抛出原始异常,不会封装AggregateException
    return readTask.GetAwaiter().GetResult();
}

注意事项

  • 标准Stream.BeginRead接口没有提供取消参数,所以调用ReadAsync时默认传入CancellationToken.None即可,无需额外处理取消逻辑
  • 代码中使用TaskContinuationOptions.ExecuteSynchronously可以避免不必要的线程调度,提升运行性能
  • EndRead中使用GetAwaiter().GetResult()而非直接访问Result,可以保证抛出的异常和ReadAsync中抛出的原始异常一致,符合APM模式的异常约定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 11:15:00