C# 8 introduced nullable reference types, which is a very cool feature. Now if you expect to get nullable values you have to write so-called guards:
There is a Guard Clauses library by Steve Ardalis that I think can help you with this situation. You can do things like:
Guard.Against.Null
(throws if input is null)Guard.Against.NullOrEmpty
(throws if string or array input is null or empty)Guard.Against.NullOrWhiteSpace
(throws if string input is null, empty or whitespace)Guard.Against.OutOfRange
(throws if integer/DateTime/enum
input is outside a provided range)Guard.Against.OutOfSQLDateRange
(throws if DateTime
input is outside the valid range of SQL Server DateTime values)Guard.Against.Zero
(throws if number input is zero)In this blog post Jason Roberts made a quick explanation of the library too.
There is another Guard Class in the Microsoft.Toolkit.Diagnostics
namespace but probably is not viable in all the use cases that will depend if wanna add that dependency to the project or not.
There are a few things you can do.
You can use [DoesNotReturnIf(...)]
in your guard method, to indicate that it throws if a particular condition is true or false, for example:
public static class Ensure
{
public static void True([DoesNotReturnIf(false)] bool condition)
{
if (!condition)
{
throw new Exception("!!!");
}
}
}
Then:
public void TestMethod(object? o)
{
Ensure.True(o != null);
Console.WriteLine(o.ToString()); // No warning
}
This works because:
[DoesNotReturnIf(bool)]
: Placed on a bool parameter. Code after the call is unreachable if the parameter has the specified bool value
Alternatively, you can declare a guard method like this:
public static class Ensure
{
public static void NotNull([NotNull] object? o)
{
if (o is null)
{
throw new Exception("!!!");
}
}
}
And use it like this:
public void TestMethod(object? o)
{
Ensure.NotNull(o);
Console.WriteLine(o.ToString()); // No warning
}
This works because:
[NotNull]
: For outputs (ref/out parameters, return values), the output will not be null, even if the type allows it. For inputs (by-value/in parameters) the value passed is known not to be null when we return.
SharpLab with examples
Of course, the real question is why you want to do this. If you don't expect value
to be null
, then declare it as object?
, rather than object
-- that's the point of having NRTs.