Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does the compiler only allow a generic returned task if I `async`/`await` it?

I’m trying to return a specifically typed value from a generic function (GenericGetter).

When I try to simply return the typed Task result from GenericGetter, the compiler is showing me the following error:

Cannot convert expression type 'System.Threading.Tasks.Task<Example.ChildClass>' 
    to return type 'System.Threading.Tasks.Task<Example.BaseClass>'

However, if I make the function that contains the GenericGetter call async, and I return the awaited value instead, the compiler doesn’t mind. It compiles, it works, but to me it seems the added async / await are redundant.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Why does GetChildClass not compile, while GetChildClassAsync does?

Here’s my example:

namespace Example
{
    public class BaseClass {}
    public class ChildClass : BaseClass {}

    public class MyExample
    {
        private async Task Main()
        {
            var foo = await GetChildClass().ConfigureAwait(false);
            var bar = await GetChildClassAsync().ConfigureAwait(false);
        }

        private Task<BaseClass> GetChildClass() =>
            GenericGetter<ChildClass>();

        private async Task<BaseClass> GetChildClassAsync() =>
            await GenericGetter<ChildClass>().ConfigureAwait(false);

        private Task<T> GenericGetter<T>()
            where T : BaseClass =>
            Task.FromResult<T>(null);
    }
}

>Solution :

In GetChildClass, you’re trying to convert a Task<ChildClass> into Task<BaseClass> – that doesn’t work, because Task<T> is invariant (as are all classes; only interfaces and delegates can be covariant or contravariant).

In GetChildClassAsync, you’re trying to convert a ChildClass into a BaseClass (which is allowed by normal inheritance and conversions) – and the C# compiler then does the wrapping into a Task<BaseClass>, so that’s fine.

I see why it appears to be redundant, and there are potentially more efficient ways that it could be done (it would be nice to be able to create a Task<SomeBaseType> from Task<SomeChildType> using a framework method/constructor) but I’d suggest just living with it.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading