【问题标题】:Should this code return a Task or Task<object>?这段代码应该返回一个任务还是任务<object>?
【发布时间】:2014-08-13 04:35:54
【问题描述】:
我正在阅读 The Nature of TaskCompletionSource,Stephen Toub 的帖子。
public static Task RunAsync(Action action)
{
var tcs = new TaskCompletionSource<Object>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
action();
tcs.SetResult(null);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
由于我们不再关心T 的类型,我默认使用Object。然后,当Action执行成功时,SetResult仍用于将Task转换为RanToCompletion最终状态;但是,由于实际结果值无关紧要,因此使用null。 最后,RunAsync 返回 Task 而不是 Task<Object>。当然,实例化的task的类型仍然是Task<Object>,但是我们不需要这样引用它,这个方法的使用者也不需要关心那些实现细节。
我不是特别明白为什么该方法应该返回Task而不是Task<object>(这就是我强调粗体的原因)。我知道该方法设置为返回Task,但tcs 是TaskCompletionSource<Object>,而不是TaskCompletionSource(我认为这是错误的)。
【问题讨论】:
标签:
c#
.net
task-parallel-library
task
taskcompletionsource
【解决方案1】:
没有非通用的TaskCompletionSource,考虑到你想要的只是没有结果的任务,结果并不重要。
在这种情况下,调用者不知道也不关心任务实际上是一个Task<object>,调用者只是awaits,如果有一个异常就会得到一个异常。调用者不知道实际结果。
Task<T> 继承自 Task 这当然有助于实现这一点
找到返回 false 的 Task<bool> 或返回 0 的 Task<int> 也很常见。
【解决方案2】:
没有用于创建Task 实例的非泛型TaskCompletionSource 类不是Task<T> 的实例。当您不关心(或不提供)返回值时,这为 TaskCompletionSource<T> 的泛型类型参数留下了两个选项:
- 使用任意现有类型,例如
object,作为返回类型。将值设置为null 表示任务完成。
- 使用特定的非公共类型,并将值设置为
null,表示任务完成。
当我创建TaskCompletionSource<T> 实例以提供没有返回值的Task 时,我更喜欢使用专用的非公共类型以确保消费代码不会将返回的Task 误认为是实例Task<T> 的结果有意义的地方。
首先,我定义了以下类(如果它嵌套在另一个类型中,它可以是 private sealed class):
internal sealed class VoidResult
{
}
然后,我不使用TaskCompletionSource<object> 作为完成源,而是使用TaskCompletionSource<VoidResult>。由于调用代码无法访问VoidResult 类型,用户将无法将Task 对象转换为Task<VoidResult> 的实例。
【解决方案3】:
我不是特别明白为什么该方法应该返回Task而不是Task<object>
因为当您返回Task<Object> 时,这意味着当此方法完成时,它将产生一些有用的Object 类型的值。在这种情况下,我们不会产生任何结果,这就是斯蒂芬选择返回 Task 的原因。
如果我们正在处理Func<Object>,那么返回Task<Object> 是合适的,因为Func 会产生一些结果,我们可以选择返回它。
为什么是TaskCompletionSource<Object>,而不是TaskCompletionSource?
因为没有这样的事情。没有非通用的TaskCompletionSource。
【解决方案4】:
如果您返回Task<object>,那么var result = await RunAsync(...) 将始终返回null,因为这就是您将结果设置为的。
客户不关心这个,所以你只返回一个Task。
理想情况下,您应该在内部使用TaskCompletionSource,而不是TaskCompletionSource<object>,并且只需调用类似SetCompleted() 的名称而不是SetResult(null)。但是这种类型不存在。