我正在阅读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
(我认为这是错误的。)
答案 0 :(得分:4)
没有非通用的TaskCompletionSource
,并且考虑到你想要的只是一个没有结果的任务,结果并不重要。
在这种情况下,调用者不知道也不关心任务实际上是Task<object>
,调用者只是await
,并且如果有,则获得异常。来电者不知道实际结果。
Task<T>
继承自Task
通常会找到返回false的Task<bool>
,或者Task<int>
返回0。
答案 1 :(得分:4)
没有用于创建TaskCompletionSource
实例的非通用Task
类,这些实例不是Task<T>
的实例。当您不关心(或未提供)返回值时,这为TaskCompletionSource<T>
的泛型类型参数留下了两个选项:
object
)作为返回类型。将值设置为null
以指示任务完成。null
以表示任务已完成。当我创建TaskCompletionSource<T>
实例以提供没有返回值的Task
时,我更喜欢使用专用的非公共类型来确保使用代码不会错误返回Task
1}}作为结果具有意义的Task<T>
的实例。
首先,我定义了以下类(如果它嵌套在另一个类型中,它可以是private sealed class
):
internal sealed class VoidResult
{
}
然后,我使用TaskCompletionSource<object>
而不是使用TaskCompletionSource<VoidResult>
作为完成源。由于调用代码无法访问VoidResult
类型,因此用户将无法将Task
对象强制转换为Task<VoidResult>
的实例。
答案 2 :(得分:2)
我并不特别理解为什么该方法应该返回
Task
而不是Task<object>
因为当您返回Task<Object>
时,这意味着当此方法完成时,它将生成一些类型Object
的有用值。在这种情况下,我们不会产生任何结果,这就是为什么斯蒂芬选择返回Task
。
如果我们正在处理Func<Object>
,那么返回Task<Object>
是合适的,因为Func
会产生一些结果,我们可能会选择退回。
为什么
TaskCompletionSource<Object>
,而不是TaskCompletionSource
?
因为没有这样的事情。没有非通用的TaskCompletionSource
。
答案 3 :(得分:1)
如果您返回了Task<object>
,则var result = await RunAsync(...)
将始终返回null
,因为这是您将结果设置为的内容。
客户并不关心这一点,因此您只需返回Task
。
理想情况下,您可以在内部使用TaskCompletionSource
,而不是使用TaskCompletionSource<object>
,只需拨打SetCompleted()
而不是SetResult(null)
。但这种类型不存在。