Nullable Reference Types are worse than useless

Mark
3 min readApr 22, 2021

Unless you’re living under a rock, you’ll be aware that most language designers (Microsoft’s included) regret the existence of null. You see, the problem with letting things be null is that most of the time you need them not to be null and this means you have to check whether they are null all over the place (‘guard conditions’)…

public void DoShit(Foo foo)
{
Console.WriteLine(foo.Bar); // If foo is null, this will error.
}
public void DoShitOrSomething(Foo foo)
{
Console.WriteLine(foo?.Bar ?? null);
}
public void DoShitSometimes(Foo foo)
{
if (foo is null)
return;
Console.WriteLine(foo.Bar);
}
public void DoShitOrElse(Foo foo)
{
if (foo is null)
throw new ArgumentNullException(nameof(foo));
Console.WriteLine(foo.Bar);
}

By nature of being stored on the stack, structs (value types) don’t permit null values, so they managed to escape this cancer. Or, at least they did escape it until some fucking troglodyte implemented default (aka null lite) and pegged the lot of us. You see, while it vaguely makes sense for a default collection to be empty, it makes a lot less sense for a default integer to be the 0 or default boolean to be false, and it makes absolutely no fucking sense for most structs to have a default. Say I have an Employee struct…

public struct Employee
{
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
}
public enum Gender {
Male,
Female,
Disabled
}

… then the ‘default’ Employee is a newborn boy named Nemo.

It’s as though someone saw all the suffering reference-type users go through and thought, “Well, fuck! Wouldn’t it be nice if we could make value-type users experience that too?” The utter stupidity of this decision is only compounded by the fact that value types already have the ability to be made nullable…

int i = 5;
int? j = null;

…and that most things that can be empty already have a static empty instance…

string foo = string.Empty();
ImmutableArray<string> foos = ImmutableArray<string>.Empty;

If you pillocks wanted to give us a typesafe way to call .Empty you should have relaxed interfaces to allow static definitions. But back to the topic at hand…

Microsoft finally decided that they were going to do something about the whole null reference problem. Too long had developers suffered under the yoke of enforced nullability! Too many of our threads had been sacrifced vainly on the altar of NullReferenceException! Enough was enough! So, in what can only be described as the quintessential Microsoft response, they did the equivalent of writing a strongly worded letter. (Irony not lost, but at least I admit that I’m just bitching about the problems!)

For the last couple of versions it’s been possible to add a directive to your project file…

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

… or class file …

#nullable enable
public class Foo
{
...
}
#nullable restore

… and the compiler ensures that you can’t use a type that might be null as though it’s not null, unless you mark it as nullable…

#nullable enable
string? foo = null; // kosher
string foo = null; // haram
#nullable restore

Except it doesn’t.

No, instead what the compiler does is warn you when you use a type that might be null as though it is null and do absolutely jack shit about external code that passes nulls to your precious little ivory tower. So…

#nullable enable
public void DoShitOrElse(Foo foo)
{
Console.WriteLine(foo.Bar);
}
#nullable restore

…is in no way equivalent to…

public void DoShitOrElse(Foo foo)
{
if (foo is null)
throw new ArgumentNullException(nameof(foo));
Console.WriteLine(foo.Bar);
}

… because external code can still pass (Foo)null to both and the second will throw an ArgumentNullException while the first will die in the arse like the little bitch it is.

This is amongst the dumbest approaches conceivable for dealing with this problem. Not only does it absolutely not achieve the same ends as guard conditions, it also fills unwitting developers with a false sense of security that they won’t need them. And, let’s be honest, if a dev is retarded enough to be caught out by a NullReferenceException, they’re definitely way too retarded to understand the implications of enabling nullable reference types.

The better approach would have been to automatically generate ArgumentNullExceptions for anything calling these methods, and the best approach would have been to completely distinguish Foo? from Foo such that you can’t pass it in place of. Also, we should have language supported…

Foo? foo = GetFoo();
//Console.WriteLine(foo.Bar); // Compile-time exception
Console.WriteLine(foo?.bar ?? string.Empty); // All good
assert foo is not null; // foo cast to Foo or errors
Console.WriteLine(foo.bar); // All good

--

--