Reputation: 257001
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
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
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:
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?
Attempt 1: Null forgiving (!) operator
No. I want to work with the type system, not hide the land-mines (as Microsoft reminds you)
Attempt 2: https://stackoverflow.com/a/58282043/12597 (gives its own error)
Attempt 3: https://stackoverflow.com/a/65076221/12597 (No, see #1)
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
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