Home > Net >  Task<T> and Nullability
Task<T> and Nullability

Time:09-03

Hi I've got the following code:

    public Task<object?> Do()
    {
        object x = new object();
        return Task.FromResult(x);
    }

This code gives me following warning CS8602 Nullability of reference types in value of type Task doesn't match target type Task<object?>

What's even more interesting is the fact that

    public Task<object?> Do()
    {
        object? x = new object();
        return Task.FromResult(x);
    }

would yield the same error.

The question is why is code analysis complaining about attempting to pass a Task with a stronger constraint as weaker one - I can't figure a scenario where it could go wrong.

CodePudding user response:

A Task<object?> is not a Task<object>, in the same sense that a List<Dog> is not a List<Animal>. That is to say, the type parameter on Task is invariant. If I do the same experiment on a type with a covariant type parameter, you can see that it works as you'd expect:

IEnumerable<object?> Foo() {
    return new List<object>(); // no warnings
}

But since nullable reference types only exist at compile time (the CLR doesn't really care), I suppose that's why the compiler only issues a warning and not an error like List<Dog> vs List<Animal>.

As for the case of

object? x = new object();
return Task.FromResult(x);

This is because the compiler actually tracks the null state of x by doing some static analysis, and sees that x is not null at the Task.FromResult call. Therefore, it infers that the type argument for the call must be:

return Task.FromResult<object>(x);

As a result, Task.FromResult would return a Task<object>, and we have the Task<object> vs Task<object?> problem again.

Oh, and don't forget that C# also can't infer object? from the return type of the method :(

These all get rid of the warning:

// specify the type parameter explicitly
public Task<object?> Do1()
{
    object? x = new object();
    return Task.FromResult<object?>(x);
}

// infer the static null analysis infer "object?"
public Task<object?> Do2()
{
    object? x = null;
    return Task.FromResult(x);
}

// infer the static null analysis infer "object?"
public Task<object?> Do3(object? x)
{
    return Task.FromResult(x);
}
  • Related