Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can/should Task<TResult> be wrapped in a C# 5.0 awaitable which is covariant in TResult?

I'm really enjoying working with C# 5.0 asynchronous programming. However, there are a few places where updating old code to be consistent with the TAP model is causing problems for me.

Here's one of them - I'm not sure exactly why Task<TResult> is not covariant in TResult, but it's causing problems for me when trying to update a covariant interface to move from a synchronous to an asychronous pattern:

Old code:

public interface IInitializable<out T> // ** out generic modifier **
{
    /// <summary>
    /// Boolean to indicate if class is ready
    /// </summary>
    bool IsInitialized { get; }

    /// <summary>
    /// Calls for instance to be initialized using current parameters
    /// Driver initialization can be done in the default constructor if desired
    /// </summary>
    T Initialize();
}

New code (won't compile):

public interface IAsyncInitializable<out T> // ** out generic modifier...broken **
{
    /// <summary>
    /// Boolean to indicate if class is ready
    /// </summary>
    bool IsInitialized { get; }

    /// <summary>
    /// Calls for instance to be initialized using current parameters
    /// Driver initialization can be done in the default constructor if desired
    /// </summary>
    Task<T> InitializeAsync(); // ** breaks because Task<TResult> is invariant in TResult **
}

Is there is a reasonable way around this without modifying my APIs too drastically? (Bonus: why is Task not covariant?). There's no IAwaitable interface, but I suppose I could make one and create an extension method that converts to a wrapped, covariant, awiatable task object. Or am I doing it wrong?

like image 709
lightw8 Avatar asked Aug 30 '12 20:08

lightw8


People also ask

Is it good to use task FromResult?

`Task. FromResult` returns an already-completed task. It's useful if you have a task-returning method (i.e., from an interface) and you want to return an already-completed task. So it's used a lot in test stubs and stuff like that.

What is Task return type in C#?

Async methods can have the following return types: Task, for an async method that performs an operation but returns no value. Task<TResult>, for an async method that returns a value. void , for an event handler. Starting with C# 7.0, any type that has an accessible GetAwaiter method.

How does task work in C#?

A Task represents some asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel. The task can return a result. There is no direct mechanism to return the result from a thread. Task supports cancellation through the use of cancellation tokens.

How do I create a new task in C#?

To start a task in C#, follow any of the below given ways. Use a delegate to start a task. Task t = new Task(delegate { PrintMessage(); }); t. Start();


2 Answers

Task<T> can't be covariant in T, because it's a class. Only interfaces and delegates can have generic variance.

As for whether it's worth doing the wrapping... I guess that depends on how much you use the covariance within your project. I suspect you'll find all the wrapping and unwrapping confusing over time, to be honest - if it's not too bad to just take the hit of removing the covariance, I'd do that.

like image 106
Jon Skeet Avatar answered Oct 18 '22 09:10

Jon Skeet


I believe that not including compiler support for the async keyword on an ITask interface was a major oversight on Microsoft's part. Luckily it's not too hard to work around this limitation.

I have implemented a covariant awaitable ITask<out TResult> interface. Its usage is pretty straightforward.

More information can be found at:

https://github.com/jam40jeff/ITask

like image 23
jam40jeff Avatar answered Oct 18 '22 10:10

jam40jeff