【问题标题】:ContinueWith for a Task of unknown generic typeContinueWith 用于未知泛型类型的任务
【发布时间】:2020-11-19 23:12:56
【问题描述】:

我有一个方法可以获取Task<T>,其中 T 在编译时未知,而IAsyncDisposable。我需要返回一个Task<T>,它会在原始任务完成后自动处理一次性对象。

这是我目前想出的,但它会引发编译时错误

private static TResult AutoDispose<TResult>(TResult result, IAsyncDisposable asyncDisposable)
{
    Func<dynamic, dynamic> continuationFunction = async t => { await asyncDisposable.DisposeAsync(); return ((dynamic)t).Result; };
    var autoDisposing = ((dynamic)result).ContinueWith(continuationFunction);
    return (TResult)autoDisposing;
}

我得到的错误是

无法将异步 lambda 表达式转换为委托类型“Func”。异步 lambda 表达式可能返回 void、Task 或 Task,它们都不能转换为 'Func'。

我已经尝试了 dynamicTask 的不同组合,但无法创建有效的解决方案。我总是遇到编译或运行时错误。

编辑

因为我为什么这样做似乎令人困惑:

我在 IAsyncQueryProviders ExecuteAsync 方法中使用它。接口定义方法签名如下

public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)

基于此,我知道TResult 属于IAsyncEnumerable&lt;T&gt;Task&lt;T&gt; 类型。我已经编写了代码来处理IAsyncEnumerable&lt;T&gt; 的情况,但是当它是Task&lt;T&gt; 时我仍然在苦苦挣扎。

【问题讨论】:

  • 请发布您使用AutoDispose 方法的示例。
  • 一般来说,将dynamic 与泛型混合使用是个坏主意。它们是非常不同的东西。我认为最好完全避免使用dynamic,因为它首先破坏了使用像 C# 这样的静态类型语言的意义。
  • "T 在编译时是未知的,并且",我实际上无法通过这个声明,它似乎相当不正确。泛型ARE在编译时编译,它们HAVE是已知的。
  • @TheGeneral T 如果您正在构建具有开放泛型的库,则在编译时是未知的。
  • @Dai 是指未绑定的泛型吗?但是,除了typeof 之外,它们不能用于任何类型的表达式...编辑...啊,好吧,取消它,我明白你在说什么。好点子。 :)

标签: c# asynchronous dynamic task continuewith


【解决方案1】:

要使用async,编译时结果类型必须是Task(或类似任务)。嵌套任务使用ContinueWithasync 延续是正常的; Unwrap 是一种解决方案。 If you can avoid ContinueWith completely, that's even better,但我相信在这种情况下,这将需要大量繁琐的泛型工作。

这样的事情应该可以工作:

private static TResult AutoDispose<TResult>(TResult result, IAsyncDisposable asyncDisposable) // where TResult: Task<T>
{
  Func<dynamic, Task<TResult>> continuationFunction = async () => { await asyncDisposable.DisposeAsync(); return result; };
  var continuation = ((dynamic)result).ContinueWith(continuationFunction, TaskScheduler.Default);
  var newResult = TaskExtensions.Unwrap(continuation);
  return (TResult)newResult;
}

【讨论】:

    【解决方案2】:

    试试这个?

    private static async Task<TResult> AutoDispose<TResult>( this Task<TResult> task )
        where TResult : IAsyncDisposable
    {
        TResult result = await task.ConfigureAwait(false);
        if( result != null )
        {
            await result.DisposeAsync().ConfigureAwait(false);
        }
        
        return result;
    }
    

    示例用法:

    Task<HttpResponseMessage> task = new HttpClient()
        .GetAsync( "/foo" )
        .AutoDispose();
    
    HttpResponseMessage resp = await task;
    // `resp` will be in a disposed state at this point.
    

    【讨论】:

    • AutoDispose 方法不能是异步的,必须返回TResult 而不是Task&lt;TResult&gt;,而且TResult 的类型为Task&lt;T&gt;,因此包装它会导致类似Task&lt;Task&lt;T&gt;&gt; 的结果。我在 AsyncQueryProviders Execute 方法中使用它,所以我有这些限制。
    猜你喜欢
    • 1970-01-01
    • 2021-01-03
    • 2022-07-06
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 2018-12-15
    • 2021-09-25
    相关资源
    最近更新 更多