I was reading The Nature of TaskCompletionSource, a post by 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;
}
Since we no longer care what the type of
T
is, I’ve defaulted to usingObject
. Then, when theAction
is executed successfully,SetResult
is still used to transition theTask
into theRanToCompletion
final state; however, since the actual result value is irrelevant,null
is used. Finally,RunAsync
returnsTask
rather thanTask<Object>
. Of course, the instantiatedtask
’s type is stillTask<Object>
, but we need not refer to it as such, and the consumer of this method need not care about those implementation details.
I don't particularly understand why the method should return Task
rather than Task<object>
(which is why I emphasised the bold sentence). I know the method is set to return Task
but tcs
is a TaskCompletionSource<Object>
, not TaskCompletionSource
(which is wrong, I think).
There isn't a non generic TaskCompletionSource
and considering all you want is a task without a result, the result doesn't matter.
The caller doesn't know and doesn't care in this case that the Task is actually a Task<object>
, The caller just await
s it, and gets an exception if there is one. The caller is unaware of the actual result.
This of course is facilitated by the fact that Task<T>
inherits from Task
It's also common to find a Task<bool>
that returns false, or Task<int>
with 0.
There is no non-generic TaskCompletionSource
class for creating instances of Task
which are not instances of Task<T>
. This leaves two options for the generic type parameter for TaskCompletionSource<T>
when you don't care about (or are not providing) the return value:
object
, as the return type. Set the value to null
to indicate completion of the task.null
to indicate completion of the task.When I create a TaskCompletionSource<T>
instance for the purpose of providing a Task
with no return value, I prefer to use a dedicated non-public type to ensure consuming code will not mistake the returned Task
as an instance of Task<T>
where the result has meaning.
First, I define the following class (it can be a private sealed class
if it's nested within another type):
internal sealed class VoidResult
{
}
Then, instead of using TaskCompletionSource<object>
for the completion source, I use TaskCompletionSource<VoidResult>
. Since the VoidResult
type is not accessible by calling code, the user will be unable to cast the Task
object to an instance of Task<VoidResult>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With