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
Tis, I’ve defaulted to usingObject. Then, when theActionis executed successfully,SetResultis still used to transition theTaskinto theRanToCompletionfinal state; however, since the actual result value is irrelevant,nullis used. Finally,RunAsyncreturnsTaskrather 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 awaits 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