Skip to content

[API Proposal]: Add MaybeNullIfNull attribute #115990

@voroninp

Description

@voroninp

Background and motivation

Sometimes several parameters of the same type affect the nullability of result value.

For example, here are the methods from CollectionExtensions:

public static TValue? GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key) =>
            dictionary.GetValueOrDefault(key, default!);

public static TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
    if (dictionary is null)
    {
         ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
    }

    return dictionary.TryGetValue(key, out TValue? value) ? value : defaultValue;
}

Pay attention to the bang (!) operator used in the first method.

API Proposal

Now Imagine we could apply the attribute to the second method:

[return: MaybeNullIfNull(namef(defultValue)]
public static TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue? defaultValue) // defaultValue is nullable now.
{
    if (dictionary is null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
    }

    return dictionary.TryGetValue(key, out TValue? value) ? value : defaultValue;
}

In this case bang operator won't be required, and compiler can infer nullability of returned value both based on the type of the dictionary and default value.

API Usage

var dict1 = new Dictionary<int, string>();
string str1 = dict1.GetValueOrDefault(1, null); // compiler emits warning/error because method can return null;
string str2 = dict1.GetValueOrDefault(1, "abc"); // compiler is happy.

var dict2 = new Dictionary<int, string?>();
string str3 = dict2.GetValueOrDefault(1, "abc"); // compiler complains.

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions