What's the best way to wrap a Task as a Task<TResult>

牧云@^-^@ 提交于 2019-12-19 02:46:27

问题


I am writing some async helper methods and I have APIs to support both Task and Task<T>. To re-use code, I'd like the Task-based API to wrap the given task as a Task<T> and just call through to the Task<T> API.

One way I can do this is:

private static async Task<bool> Convert(this Task @this)
{
    await @this.ConfigureAwait(false);
    return false;
}

However, I'm wondering: is there there is a better/builtin way to do this?


回答1:


There is no existing Task method that does exactly this, no. Your method is fine, and is likely about as simple as you'll be able to get.

Implementing the proper error propagating/cancellation semantics using any other method is deceptively hard.




回答2:


Updated, the following propagates exceptions and cancellation:

public static class TaskExt
{
    public static Task<Empty> AsGeneric(this Task @this)
    {
        return @this.IsCompleted ?
            CompletedAsGeneric(@this) :
            @this.ContinueWith<Task<Empty>>(CompletedAsGeneric, 
                TaskContinuationOptions.ExecuteSynchronously).Unwrap();
    }

    static Task<Empty> CompletedAsGeneric(Task completedTask)
    {
        try
        {
            if (completedTask.Status != TaskStatus.RanToCompletion)
                // propagate exceptions
                completedTask.GetAwaiter().GetResult();

            // return completed task
            return Task.FromResult(Empty.Value);
        }
        catch (OperationCanceledException ex)
        {
            // propagate cancellation
            if (completedTask.IsCanceled)
                // return cancelled task
                return new Task<Empty>(() => Empty.Value, ex.CancellationToken);
            throw;
        }
    }
}

public struct Empty
{
    public static readonly Empty Value = default(Empty);
}



回答3:


I've had the same requirement recently and I solved it with my own helper extension method, which allows the user to effectively wrap a Task with a Task<T>:

public static async Task<TResult> WithCompletionResult<TResult>(
    this Task sourceTask,
    TResult result
)
{
    await sourceTask;
    return result;
}

In your example call with:

Task<bool> task = myTask.WithCompletionResult<bool>(false);

If the result of Task<T> does not matter, I will use:

Task<object> task = myTask.WithCompletionResult<object>(null);

I hope this helps. If anyone knows of a pitfall with this approach let me know!




回答4:


Using await seems a bit overkill here. No need for the state machine here, just use a ContinueWith

private static Task<bool> Convert(this Task @this)
{
    return @this.ContinueWith(p => { p.Wait(); return false;});
}

Note: This will result in an AggregateException being wrapped unfortunately



来源:https://stackoverflow.com/questions/22541734/whats-the-best-way-to-wrap-a-task-as-a-tasktresult

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!