简体   繁体   中英

.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?

If "Key" isn't found in the dictionary, then a value of null will be assigned to the value variable. However you've declared value as string , meaning that it shouldn't contain null . Therefore the compiler is giving you a warning.

The fact that you've initially assigned string.Empty to value doesn't matter - that will always get overwritten by TryGetValue (and you should get another warning which says that).

You should declare value as a string? , to indicate that its value might be null .

Note that the compiler's pretty smart. If you write:

if (!dictionary.TryGetValue("Key", out string? value))
{
    value = string.Empty;
}

then the compiler knows that value cannot be null , and it won't complain if you then try and call methods on it.

canton7's answer is correct (+1).
This is not an explanation but a workaround:
You could add an extension method to Dictionary<TVey, TValue> like this:

public static bool TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue @default, out TValue @value) where TKey : notnull
{
    var result = dictionary.TryGetValue(key, out var val);
    @value = result ? val : @default;
    return result;
}

Then you can use it like this:

private static void Method()
{
    var dictionary = new Dictionary<string, string>();
    /// populate dictionary here...

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

This should enable you to keep value as a non-nullable string.

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM