问题
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