Question

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 using Object. Then, when the Action is executed successfully, SetResult is still used to transition the Task into the RanToCompletion final state; however, since the actual result value is irrelevant, null is used. Finally, RunAsync returns Task rather than Task<Object>. Of course, the instantiated task’s type is still Task<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).

Was it helpful?

Solution

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.

OTHER TIPS

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:

  1. Use an arbitrary existing type, such as object, as the return type. Set the value to null to indicate completion of the task.
  2. Use a specific non-public type, and set the value to 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>.

I don't particularly understand why the method should return Task rather than Task<object>

Because when you return Task<Object> it means that when this method completes it will produce some useful value of type Object. in this case we're not producing any result, That's why stephen choose to return Task.

If we're dealing with Func<Object> then returning Task<Object> would be appropriate, as Func will produce some result, we may choose to return it.

Why TaskCompletionSource<Object>, not TaskCompletionSource?

Because there's no such thing. There is no non generic TaskCompletionSource.

If you returned a Task<object>, then var result = await RunAsync(...) would always return null, since that's what you're setting the result to.

The client doesn't care about this, so you just return a Task.

Ideally, you would use a TaskCompletionSource internally, instead of a TaskCompletionSource<object>, and just call something like SetCompleted() instead of SetResult(null). But such type does not exist.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top