ContinueWith 用于未知通用类型的任务
ContinueWith for a Task of unknown generic type
我有一个方法可以得到一个 Task<T>
和一个 IAsyncDisposable
,其中 T 在编译时是未知的。我需要 return 一个 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;
}
我遇到的错误是
Cannot convert async lambda expression to delegate type 'Func<dynamic, dynamic>'. An async lambda expression may return void, Task or Task, none of which are convertible to 'Func<dynamic, dynamic>'.
我已经尝试了 dynamic
和 Task
的不同组合,但无法创建有效的解决方案。我总是遇到编译或运行时错误。
编辑
因为我这样做的原因似乎令人困惑:
我在 IAsyncQueryProvider
s ExecuteAsync
方法中使用它。接口定义方法签名如下
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
基于此,我知道 TResult
是 IAsyncEnumerable<T>
或 Task<T>
类型。我已经编写了代码来处理 IAsyncEnumerable<T>
的情况,但当它是 Task<T>
.
时我仍在苦苦挣扎
试试这个?
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.
要使用 async
,编译时结果类型必须是 Task
(或类似任务)。使用 ContinueWith
和 async
延续的嵌套任务是正常的; 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;
}
我有一个方法可以得到一个 Task<T>
和一个 IAsyncDisposable
,其中 T 在编译时是未知的。我需要 return 一个 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;
}
我遇到的错误是
Cannot convert async lambda expression to delegate type 'Func<dynamic, dynamic>'. An async lambda expression may return void, Task or Task, none of which are convertible to 'Func<dynamic, dynamic>'.
我已经尝试了 dynamic
和 Task
的不同组合,但无法创建有效的解决方案。我总是遇到编译或运行时错误。
编辑
因为我这样做的原因似乎令人困惑:
我在 IAsyncQueryProvider
s ExecuteAsync
方法中使用它。接口定义方法签名如下
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
基于此,我知道 TResult
是 IAsyncEnumerable<T>
或 Task<T>
类型。我已经编写了代码来处理 IAsyncEnumerable<T>
的情况,但当它是 Task<T>
.
试试这个?
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.
要使用 async
,编译时结果类型必须是 Task
(或类似任务)。使用 ContinueWith
和 async
延续的嵌套任务是正常的; 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;
}