C# Infer Nullability of Member Assigned Through Ref Parameter During Construction

Viewed 145

I have some shorthand for setting property values in a Xamarin app:

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
    if (Object.Equals(storage, value))
        return false;
    storage = value;
    OnPropertyChanged(propertyName);
    return true;
}

This is used in my ViewModels, for example:

class PersonViewModel : ViewModel
{
    private string name;
    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }
    public PersonViewModel(string theName) {
        Name = theName;
    }
}

When I have #nullable enable, how can I (in the general case, whenever SetProperty is used) help the compiler infer that the name member is not null after construction?

Currently, I get a warning on the constructor which says: "CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.".

I'm using <LangVersion>latest</LangVersion> in my .csproj.

2 Answers

I think you need to use NotNullIfNotNull to say that "storage is not null if value is not null".

See SharpLab

protected bool SetProperty<T>(
    [NotNullIfNotNull("value")] ref T storage,
    T value,
    [CallerMemberName] string? propertyName = null)
{
    if (object.Equals(storage, value))
        return false;
    storage = value;
    OnPropertyChanged(propertyName);
    return true;
}

In the constructor, you need to do:

SetProperty(ref name, theName);

rather than setting the property normally, because the compiler doesn't know that these two are the same thing.

Edit: I just noticed that in .NET 5.0, they added MemberNotNull, so your constructor can stay like this:

public PersonViewModel(string theName)
{
    Name = theName;
}

You just have to mark the setter with the attribute:

public string Name
{
    get => name;
    [MemberNotNull(nameof(name))]
    set => SetProperty(ref name, value);
}

Well, as already said, the compiler does not know how Name and name are connected, so you should set the name property exactly how you would in Name setter. But this won't get rid out of warning, but you can use null-forgiving operator !, see samlpe code:

public class Cls
{
    public Cls(string n)
    {
        SetValue(ref name!, n);
    }

    private string name;
    public string Name
    {
        get => name;
        set => SetValue(ref name, value);
    }
    private void SetValue<T>(ref T store, T value) => store = value;
}
Related