C# 8+ Nullable Reference checking: Can Static Analysis work out that either this field or that field is non-null?

Viewed 196

Consider a class with 2 constructors. Let's say that one constructor accepts a dependency, whilst the other accepts a factory for the dependency to be got later:

class ThereCanBeOnlyOneButAtLeastOne
{
    readonly Thing? knownThing;
    readonly ThingFactory? factory;
    public ThereCanBeOnlyOneButAtLeastOne(ThingFactory factory) => this.factory = factory;
    public ThereCanBeOnlyOneButAtLeastOne(Thing knownThing) => this.knownThing = knownThing;

    public OtherThing CalculateSomething()
    {
        var theThing = knownThing??factory?.Get();

I would like the static analysis to have worked out that theThing is guaranteed NotNull at this point because at least one of the constructors must have been called. But it doesn't.

Can it?

1 Answers

The 2021 consensus seems to be: No, it can't, you have to tell it.

There is some nearly-relevant functionality in C# 9:

    [MemberNotNullWhen(true, nameof(knownThing))]
    [MemberNotNullWhen(false, nameof(factory))]
    public bool HasKnownThing => knownThing != null;

which might lead you to think that

var theThing = HasKnownThing ? knownThing : factory.Get();

is provably non-null, but the static analysis works out that it doesn't really know that factory is non-null:

Scratch.cs(38, 38): [CS8775] Member 'factory' must have a non-null value when exiting with 'false'

and however you twist, it still comes down to:

The static analysis can't work out for itself that at least one of the fields is non-null, you have to tell it. And the minimal way to tell it is probably:

theThing = knownThing ?? factory!.Get()

whether do this in a variable or a field or a private property or method.

Related