What is Null?
Null is an adjective that means “without value”. It is the absence of knowledge. It is “I dunno”.
Null sounds like a very computer-y thing, but it’s something we encounter in everyday life away from our computers, we just don’t realize it. For example, let’s say someone tells us “I have a new friend!”. At this point in the conversation, every descriptor of the “new friend” has a value of null because we don’t know any specifics about this friend.
Null exceptions in programming are common because it’s very easy for us to forget the context of how something was created and assume that the thing we created has all their properties with values assigned.
A NullReferenceException
is C# saying “You want me to act like there’s something there, but there’s nothing there.”
List<int> numbersList;
numbersList.Add(4); //This causes a NullReferenceException because even though we name the variable, we never actually create a list.
We can be more aware of when we might causes a NullReferenceException
by enabling nullable contexts (https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts) in our project. This attribute is neat because it does not change the behavior of compiler, it changes the behavior of the developer. It allows us to more explicitly express our intent when it comes to values and their nullability. C# 8.0 introduced nullable reference types to allow us to more clearly show when we intend for a value to be null (reference types are already nullable).
How do we better express our intent when it comes to null values?
Declare that the type is nullable with ?
Adding a ? to the end of a type explicitly declares to future programmers that the type of this variable is nullable and that it’s ok if it’s null sometimes.
How do we use it?
Thingy? thing;
This says “Hey, ‘thing’ is of type ‘Thingy’ but it might be null and that might be expected. Just letting you know.”
Note: Reference types (string, custom types like Thingy
, etc.) are already nullable so Thingy thing;
is allowed, but with nullable contexts enabled we will get a warning because we are not explicitly saying that we’re cool with the variable thing
being null.
Null-forgiving operator (!)
The null-forgiving operator generally says “I know this potentially could cause a NullReferenceException
, but because we know what is going on, we can assure the compiler that it won’t. We promise.”
How do we use it?
Thingy? thing = null!;
This says “Look, we’re gonna have a variable named ‘thing’ and it’s null right now, but don’t freak out. We won’t cause a NullReferenceException
. We promise.”
Thingy thing = GoGetTheThing()!;
public Thingy? GoGetTheThing()
{
//Let's pretend we connect to a database and we know that it contains the Thingy we're looking for, but for some reason we made this function return a nullable Thingy just in case.
return _database.GetThing();
}
This says “I know the GoGetThing function might return null but we PROMISE it won’t.”
Null-coalescing operator (??)
The null-coalescing operator is great for when we want to do something if a value is null.
How do we use it?
Console.WriteLine($"Hey {newFriend.Name ?? "Buddy"}! How have you been?");
This says “If newFriend’s Name is null, then use “Buddy” instead.
Null-conditional operator (Also known as the Elvis operator) (?.)
The null-conditional operator lets us do things with values that might be null in a safer way.
How do we us it?
If we were to do…
int? nameLength = newFriend.Name.Length;
… and newfriend.Name
was null then we’d get a NullReferenceException because it’s impossible to get the length of null.
BUT if we were to do…
int? nameLength = newFriend.Name?.Length;
… and `newFriend.Name` was null, then we’d just assign null to nameLength
and go on with our day.
Note: I think we should consider renaming it from “Elvis operator” and use someone who’s been relevant to pop culture within the last 40 years. I don’t know who that is though cuz I am also not anywhere close to being up-to-date with pop culture.
Note: Since these attributes try to change the behavior of the developer and not the compiler, we could still fail to fulfill our promises we made and end up with null which means that now, not only are NullReferenceException annoying but now they make us feel bad because we broke our promise to the compiler.
For way more about most things null in C#, check out https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references
A good course for using null in C# 8.0: https://learn.microsoft.com/en-us/training/modules/csharp-null-safety/
A good course for using null in C# 8.0: https://learn.microsoft.com/en-us/training/modules/csharp-null-safety/