Search code examples
c#nullable-reference-types

Why does intellicode '... is not null' when it is cleary null?


Edit:

Thanks to canton7 I will be looking into nullable reference types as this is the C# 8 feature which is shown here.

Edit2:

TL;DR: The message "'_products' is not null here." is purely based on the definition of the variable _products. If it's defined as a nullable-reference-type it sais "maybe be null here" if it's defined as NON-nullable-reference-type(=normal, basic def without the '?') it will say, what is shown on the picture below. Both cases are only hints/warnings and won't change the compiled code.

Original Question:

Calling method:

protected override async Task OnInitializedAsync() //ignore the Polymorphism
{
    _products = await ProdService.GetProductListAsync();
    Console.WriteLine("_products: " + _products + " | is null?: " + (_products == null));
//output: <<_products:   is null? True>>
}

Image:

enter image description here

Called method:

public async Task<List<Product>> GetProductListAsync()
{
    return null;
}

Image:

enter image description here


Solution

  • You have the C# 8 feature "Nullable Reference Types" (NRTs) enabled. This adds a layer of static analysis, which tells you when a reference type might be null, and warns you if you dereference something which might be null. See this doc and this MSDN blog post.

    You can disable NRTs by setting <Nullable>disable</Nullable> in your .csproj.

    The signature Task<List<Product>> GetProductListAsync() promises that this method will return a non-null Task, containing a non-null List of non-null Products.

    Any code which calls this method only sees the signature. The compiler trusts that you wrote the signature correctly. Since the signature says that this method does not return null, this means that _products is shown as "not null".

    (Think about what would happen if this method was defined in a different DLL. The compiler doesn't have the source code available to go analysing it. This sort of whole-program type inference is also very costly and fragile: it's much better to have interfaces explicitly stated by the programmer, rather than inferred.)

    However, you have broken this promise in the implementation of the method, where you do return null. This is where the compiler moans at you. The CS8603 in your final screenshot shows the compiler complaining because the method signature promises that you won't return null, and yet you have return null.

    If the method really can return null, then its signature should be:

    Task<List<Product>?> GetProductListAsync()
    

    This will make the warning on the return null go away, and _products will be shown as "maybe null".