Search code examples
c#.net.net-corec#-8.0nullable-reference-types

.NET non-nullable reference type and out parameters


I modified my csproj file to enable null reference types in C#8:

<Nullable>enable</Nullable>

Given the following code:

private static void Method()
{
    var dictionary = new Dictionary<string, string>();
    string value = string.Empty;

    dictionary.TryGetValue("Key", out value);
}

The line with TryGetValue() gives the warning:

CS8600: Converting null literal or possible null value to non-nullable type.

I don't understand why. The signature of TryGetValue() is :

public bool TryGetValue(string key, [MaybeNullWhen(false)] out string value);

The code example has only non-nullable references. Why is it getting this error?


Solution

  • From the documentation Attributes for null-state static analysis interpreted by the C# compiler.

    In a nullable enabled context, the compiler performs static analysis of code to determine the null-state of all reference type variables:

    • not-null: Static analysis determines that a variable has a non-null value.
    • maybe-null: Static analysis can't determine that a variable is assigned a non-null value.

    The static analyzer consider a variable can be :

    • nullable
    • not nullable
    • maybe nullable

    When a not nullable variable is decorated with the attribute MaybeNull, the static analyser consider the variable is maybe nullable.

    [return: MaybeNull]
    static string Find(string key)
    {
        return key == "" ? null : key;
    }
    
    string value1 = Find("key"); // Warning CS8600 Converting null literal or possible null value to non-nullable type.
    string? value2 = Find("key"); // No warning
    var value3 = Find("key"); // The inferred type is 'string?'
    

    The MaybeNullWhen attribute is similar, but the static analyser can handle checks based on the result of the method.

    static bool TryGetValue(string key, [MaybeNullWhen(false)] out string value)
    {
        if(key == "")
        {
            value = null;
            return false;
        }
        value = "Foo";
        return true;
    }
    
    string notnullable;
    
    string value1;
    if (TryGetValue("Key", out value1)) // Warning CS8600 Converting null literal or possible null value to non-nullable type.
        notnullable = value1;
    else
        notnullable = value1; // Warning CS8600 Converting null literal or possible null value to non-nullable type.
    
    string? value2;
    if (TryGetValue("Key", out value2))
        notnullable = value2;
    else
        notnullable = value2; // Warning CS8600 Converting null literal or possible null value to non-nullable type.
    

    I agree, that makes no sense in this examples. But with a generic method, you can specify a non nullable type while the method can return/set null:

    [return: MaybeNull]
    static T Find<T>(string key);
    static bool TryGetValue<T>(string key, [MaybeNullWhen(false)] out T value)