Ian Boyd
Ian Boyd

Reputation: 257001

How to tell C# nullable type checker that a variable cannot be null after a function call returns?

Short Version

How to make the C# nullable type checking compiler realize that the variable customer cannot be null after the call returns:

Constraints.NotNull(customer);

.NET Fiddle: https://dotnetfiddle.net/ZcgRCV

Long Version

If i have code like:

#nullable enable

Customer? c;
...
Console.WriteLine(customer.FirstName);

The compiler will (correctly) warn me that customer might be null when i try to access .FirstName:

customer.FirstName ⇐ possible null dereference

Guard Constraint

I want to perform a guard, or a constraint that will tell the C# 8 nullable type checker that the value cannot be null. I'll call a function that guarantess that the variable cannot be null after the function returns:

#nullable enable

Customer? customer;
...
Constraint.NotNull(customer, nameof(customer)); //*if* this function returns, customer is *definitely* not null
Console.WriteLine(customer.FirstName);

Where Constraint.NotNull is something like:

public static class Constraint
{
    public static void NotNull(Object? o, String msg="")
    {
       if (o == null) 
          throw new Exception("Object cannot be null "+msg);
    }
}

And the guard does its job; it raises an exception. But the compiler doesn't realize that customer cannot be null after Constrant.NotNull returns:

enter image description here

Another Example

A better example, i came across in some old code:

Customer? currentCustomer;

//...

EnsureCurrentCustomer(); 

DoSomethingWithIt(currentCustomer); // guaranteed not null because EnsureCurrentCustomer did it,
             // but the compiler doesn't know that.

We need a way for EnsureCurrentCustomer to tell the C# 8 nullable type checker that the variable currentCustomer cannot be null after EnsureCurrentCustomer function returns.

How do?


ChatGPT says the only way to do it is to use the JetBrains [NotNull] constraint; which i don't have access to.

Update: ChatGPT4o explained the use of System.Diagnostics.CodeAnalysis.NotNullAttribute. The documentation does not give any hint that it can be used in this way. But once ChatGPT explained it, with an example, then PSGuy's incomplete partial answer made sense. So i fleshed out his answer and accepted. Thanks ChatGPT!

Upvotes: 3

Views: 1644

Answers (1)

PSGuy
PSGuy

Reputation: 749

This is achieved using the [NotNull] annotation on the argument being verified in your guard-clause method:

public static class Constraint
{
    public static void NotNull([NotNull] object? o, string msg = "")
    {
        if (o == null)
        {
            throw new Exception("Object cannot be null " + msg);
        }
    }
}

And then usage:

Customer? customer = GetCustomer();
Constraint.NotNull(customer, nameof(customer)); // 'customer' may be null here

Console.WriteLine(customer.FirstName);          // 'customer' is not null here

A good reference for this is in the ArgumentNullException.ThrowIfNull static method. The summary of the NotNullAttribute says:

Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns.

You can also refer to xUnit's Assert.NotNull assertion, which does the same thing.

Upvotes: 2

Related Questions