I've recently seen the following code:
public class Person
{
//line 1
public string FirstName { get; }
//line 2
public string LastName { get; } = null!;
//assign null is possible
public string? MiddleName { get; } = null;
public Person(string firstName, string lastName, string middleName)
{
FirstName = firstName;
LastName = lastName;
MiddleName = middleName;
}
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
MiddleName = null;
}
}
Basically, I try to dig into new c# 8 features. One of them is NullableReferenceTypes
.
Actually, there're a lot of articles and information about it already. E.g. this article is quite good.
But I didn't find any information about this new statement null!
Can someone provide me an explanation for it?
Why do I need to use this?
And what is the difference between line1
and line2
?
The key to understanding what null!
means is understanding the !
operator. You may have used it before as the "not" operator. However, since C# 8.0 and its new "nullable-reference-types" feature, the operator got a second meaning. It can be used on a type to control Nullability, it is then called the "Null Forgiving Operator"
Assuming this definition:
class Person
{
// Not every person has a middle name. We express "no middle name" as "null"
public string? MiddleName;
}
The usage would be:
void LogPerson(Person person)
{
Console.WriteLine(person.MiddleName.Length); // WARNING: may be null
Console.WriteLine(person.MiddleName!.Length); // No warning
}
This operator basically turns off the compiler null checks for this usage.
C# 8.0 tries to help you manage your null
-values. Instead of allowing you to assign null
to everything by default, they have flipped things around and now require you to explicitly mark everything you want to be able to hold a null
value.
This is a super useful feature, it allows you to avoid NullReferenceException
s by forcing you to make a decision and enforcing it.
There are 2 states a variable can be in - when talking about null-safety.
Since C# 8.0 all reference types are non-nullable by default. Value types have been non-nullable since C# 2.0!
The "nullability" can be modified by 2 new (type-level) operators:
!
= from Nullable
to Non-Nullable
?
= from Non-Nullable
to Nullable
These operators are counterparts to one another. The Compiler uses the information, you define with those operators, to ensure null-safety.
?
Operator usage.This operator tells the compiler that a variable can hold a null value.
Nullable string? x;
x
is a reference type - So by default non-nullable.?
operator - which makes it nullable.x = null
Works fine.Non-Nullable string y;
y
is a reference type - So by default non-nullable.y = null
Generates a warning since you assign a null value to something that is not supposed to be null.Nice to know: Using string?
is syntactic sugar for System.Nullable<string>
!
Operator usage.This operator tells the compiler that something that could be null, is safe to be accessed. You express the intent to "not care" about null safety in this instance.
string x;
string? y;
x = y
Warning: "y" may be null
x = y!
y
is a reference type with the ?
type modifier applied so it is nullable if not proven otherwise.!
to y
which overrides its nullability settings to make it non-nullableWARNING The
!
operator only turns off the compiler-checks at a type-system level - At runtime, the value may still be null.
You should try to avoid using the Null-Forgiving-Operator, usage may be the symptom of a design flaw in your system since it negates the effects of null-safety you get guaranteed by the compiler.
Using the !
operator will create very hard to find bugs. If you have a property that is marked non-nullable, you will assume you can use it safely. But at runtime, you suddenly run into a NullReferenceException
and scratch your head. Since a value actually became null after bypassing the compiler-checks with !
.
There are valid use-cases (outlined in detail below) where usage is appropriate. However, in 99% of the cases, you are better off with an alternative solution. Please do not slap dozens of !
's in your code, just to silence the warnings.
null
comes through.null!
mean?It tells the compiler that null
is not a nullable
value. Sounds weird, doesn't it?
It is the same as y!
from the example above. It only looks weird since you apply the operator to the null
literal. But the concept is the same. In this case, the null
literal is the same as any other expression/type/value/variable.
The null
literal type is the only type that is nullable by default! But as we learned, the nullability of any type can be overridden with !
to non-nullable.
The type system does not care about the actual/runtime value of a variable. Only its compile-time type and in your example the variable you want to assign to LastName
(null!
) is non-nullable
, which is valid as far as the type-system is concerned.
Consider this (invalid) piece of code.
object? null;
LastName = null!;