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);
}