我有一个方法,GetSomethingAsync,那就是return Task<MyTypeA>.Run(() => GetSomething());
我有另一个方法,GetSomethingElseAsync,那就是return Task<MyTypeB>.Run(() => GetSomethingElse());
我希望第二个任务有条件地运行,基于第一个任务中的一些内容,所以我有
var task1 = GetSomethingAsync();
var task2 = task1.ContinueWith(x =>
x.Result == null ? Task.FromResult(null) : GetSomethingElseAsync());task2编译为Task<Task<MyTypeB>>。我在等Task<MyTypeB>。有可能得到我预期的结果吗?
发布于 2018-06-06 19:19:40
假设
var task1 = GetSomethingAsync();使用异步定义并返回一个Task<T>。
ContinueWith()的签名是
public Task<TResult> ContinueWith<TResult>(
Func<Task, TResult> continuationFunction
)现在关注您的lambda结果:
x => x.Result == null ? Task.FromResult(null)如果任务的结果为null,则返回一个任务(Task.FromResult)。
因此,您的Func<Task, TResult> TResult是一个Task<T>,就好像lambda是按以下方式编写的:
Task<T> AnonymousFunction(task x)
{
return ... Task.FromResult(null);
}现在,ContinueWith()的结果是一个Task<TResult>,既然我们已经确定TResult是一个Task<T>,那么结果类型就是Task<Task<T>>。
发布于 2022-10-09 17:37:16
您需要的是Unwrap扩展方法。此方法接受嵌套的任务Task<Task<T>>,并将其扁平化为Task<T>。未包装任务表示外部和内部任务的完成。如果其中任何人失败,则未包装的任务包含发生在任一任务中的Exception:
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = task1.ContinueWith(t =>
{
return t.Result is null ?
Task.FromResult<MyTypeB>(null) : GetSomethingElseAsync();
}).Unwrap();不幸的是,上面的代码远非完美。
task1的取消传播为失败。因此,如果是task1.IsCanceled,则task2将是IsFaulted。task1的可能异常封装在嵌套的AggregateException中。下面是更正的相同代码:
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = task1.ContinueWith(t =>
{
if (t.IsFaulted)
return Task.FromException<MyTypeB>(t.Exception.InnerException);
if (t.IsCanceled)
{
TaskCompletionSource<MyTypeB> tcs = new();
tcs.SetCanceled(new TaskCanceledException(t).CancellationToken);
return tcs.Task;
}
return t.Result is null ?
Task.FromResult<MyTypeB>(null) : GetSomethingElseAsync();
}, TaskScheduler.Default).Unwrap();一个更简单的解决方案是抛弃繁琐的ContinueWith方法,转而使用方便的异步/等待语言特性:
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = ((Func<Task<MyTypeB>>)(async () =>
{
MyTypeA result = await task1.ConfigureAwait(false);
return result is null ? null :
await GetSomethingElseAsync().ConfigureAwait(false);
}))();这可以通过使用Then系列的扩展方法来进一步简化,这些方法可以找到这里 (由Stephen编写)。
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = task1.Then(async result =>
result is null ? null : await GetSomethingElseAsync().ConfigureAwait(false));您可以将Then扩展方法看作是一个ContinueWithResult,其中包含一个参数,该参数包含先导任务的结果,而不是前置任务本身。
发布于 2018-06-06 19:27:19
我相信这就是你想要的:
var task2 = task1.ContinueWith(completedTask => completedTask.Result == null ? (TypeB)null : GetSomethingElse());task2现在应该是Task<MyTypeB>类型。
问题是task1.ContinueWith已经返回了一个Task<T>。在您的语句中,您还将返回一个Task<T>,或者更准确地说,返回一个Task<MyTypeB>。因此,调用task1.ContinueWith的结果是Task<T>,其中T是Task<MyTypeB>。您在调试器中看到的是:Task<Task<MyTypeB>>。
https://stackoverflow.com/questions/50727865
复制相似问题