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:
object? value = null;
if (value is null)
{
throw new ArgumentNullException();
}
…
These can be a bit repetitive. What I am wondering is if it is possible to avoid writing this type of code for every variable, but instead have a guard-type static void
function that throws exception if value is null
or just returns if value is not null
. Or is this too hard for compiler to infer? Especially if it's external library/package?
Nullable contexts enable fine-grained control for how the compiler interprets reference type variables. The nullable annotation context determines the compiler's behavior.
Nullable reference types aren't new class types, but rather annotations on existing reference types. The compiler uses those annotations to help you find potential null reference errors in your code. There's no runtime difference between a non-nullable reference type and a nullable reference type.
In C# 8.0, strings are known as a nullable “string!”, and so the AllowNull annotation allows setting it to null, even though the string that we return isn't null (for example, we do a comparison check and set it to a default value if null.)
In general, a null attribute value is the equivalent of nothing. However, it's important to be precise in our terminology because there are many ways to represent nothing: An attribute has a value that indicates nothingness (null) An attribute exists but has no value (empty) An attribute doesn't exist (missing)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With