Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MaybeNull attribute warns on null reference return

As part of moving towards C# 8 nullable reference types, I encountered the following code (simplified):

public string GetIfExists(string key)
{
    dict.TryGetValue(key, out var value);

    return value;
}

The return line warns on a possible null reference return, and it makes sense. So I tried to annotate the method with a [return: MaybeNull] attribute, but the warning remained to my surprise. From the documentation, I understand that this attribute marks the return type as optional null, even when the actual type doesn't allow it.

It seems that my only option to avoid warnings is to mark the return type string?. So what is the use of [return: MaybeNull]?

like image 279
Tomer Avatar asked Jan 01 '20 13:01

Tomer


People also ask

What is NotNull in C#?

NotNull: A nullable field, parameter, property, or return value will never be null. MaybeNullWhen: A non-nullable argument may be null when the method returns the specified bool value. NotNullWhen: A nullable argument won't be null when the method returns the specified bool value.

What is nullable context C#?

The nullable annotation context determines the compiler's behavior. There are four values for the nullable annotation context: disable: The compiler behaves similarly to C# 7.3 and earlier: Nullable warnings are disabled. All reference type variables are nullable reference types.

Is string nullable C#?

In C# 8.0, strings are known as a nullable “string!”, and so the AllowNull annotation allows setting it to null, even though the string that we return isn't null (for example, we do a comparison check and set it to a default value if null.)


2 Answers

From the documentation:

[return: MaybeNull] informs callers that the contract implies a non-nullable type, but the return value may actually be null. Use the MaybeNull attribute when your API should be a non-nullable type, typically a generic type parameter, but there may be instances where null would be returned.

This means that you want to use the attribute when you want the warning to be shown without changing the contract.

It seems that the attribute has been created to help enforce non-nullability (propagating a warning that forces the user to check for null) in two scenarios:

  • in situations where it is not possible to change the signature of a function.
  • in situations in which generics are involved. Generics are, due to their nature, a bit more difficult to handle and you might find yourself in a situation in which you are not allowed to have a T?, but you would still like the users of the method to be aware that the result needs to be checked.

The only way you have to get rid of the warning is to change the return type to string? (or return value!;, but it would be a lie :p).

like image 97
Alvin Sartor Avatar answered Jan 03 '23 08:01

Alvin Sartor


Your issue is irreproducible.

public string GetIfExists ( string key )
{
  new Dictionary<string, string>().TryGetValue (key, out string value); // Warning  CS8600  Converting null literal or possible null value to non-nullable type.
  return value; // Warning  CS8603  Possible null reference return.
}
[return: MaybeNull]
public string GetIfExists ( string key )
{
  new Dictionary<string, string>().TryGetValue (key, out string? value);
  return value;
}

(No issue‼)


From usage perspective for generics there is no difference between T? return type and [return: MaybeNull]. (Applies also to reference type.)

[return: MaybeNull]
public T Find<T> ( ) => default;
public T? Find2<T> () => default;    
public T Find3<T> ( ) => default;  // Warning   CS8603  Possible null reference return.

void Test ()
{
  object x = Find<object> (); // Warning    CS8600  Converting null literal or possible null value to non-nullable type.
  object x2 = Find2<object> (); // Warning  CS8600  Converting null literal or possible null value to non-nullable type.
  object x3 = Find3<object> ();
}
like image 27
Yarl Avatar answered Jan 03 '23 07:01

Yarl