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 din't find any information about this new statement null!
Can someone provide to me explanation for it ? Why I need to use this ?
And what is difference between line1
and line2
?
What is the
!
operator when used on a type?The
!
operator, when used on a type, is called the Null Forgiving Operator [docs]. It was introduced in C# 8.0Technical Explanation
Typical usage
Assuming this definition:
The usage would be:
This operator basically turns off the compiler null checks.
Inner workings
Using 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.
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.
The "nullability" can be modified by these 2 new type-operators:
!
= FromNullable
toNon-Nullable
?
= FromNon-Nullable
toNullable
These operators are basically counterparts to one another. The Compiler uses the information - you define with those operators - to ensure null-safety.
?
Operator usage.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.x = null
Generates a warning since you assign a null value to something that is not supposed to be null.!
Operator usage.x = y
Warning: "y" may be null
x = y!
y!
Applies the!
operator toy
which makes it non-nullable.This is an Anti-Pattern.
You should try to avoid using the
!
Null-Forgiving-Operator.There are valid use-cases ( outlined in detail below ) like unit-tests where this operator is appropriate to use. In 99% of the cases though, you are better off with an alternative solution. Please do not slap dozens of
!
's in your code, just to silence the warnings. Think if your cause really warrants the use.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 aNullReferenceException
and scratch your head. Since a value actually became null after bypassing the compiler-checks with!
.Why does this operator exist then?
null
comes through.Answering your question specifically.
So what does
null!
mean?It tells the compiler that
null
is not anull
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 thenull
literal. But the concept is the same.Picking apart what is happening.
This line defines a non-nullable class property named
LastName
of typestring
. Since it is non-nullable you can technically not assign null to it - obviously.But you do just that by using the
!
operator. Becausenull!
is not null - as far as the compiler is concerned about null-safety.When the "nullable reference types" feature is turned on, the compiler tracks which values in your code it thinks may be null or not. There are times where the compiler could have insufficient knowledge.
For example, you may be using a delayed initialization pattern, where the constructor doesn't initialize all the fields with actual (non-null) values, but you always call an initialization method which guarantees the fields are non-null. In such case, you face a trade-off:
null!
), then the field can be used without null check.Note that by using the
!
suppression operator, you are taking on some risk. Imagine that you are not actually initializing all the fields as consistently as you thought. Then the use ofnull!
to initialize a field covers up the fact that anull
is slipping in. Some unsuspecting code can receive anull
and therefore fail.More generally, you may have some domain knowledge: "if I checked a certain method, then I know that some value isn't null":
Again, you must be confident of your code's invariant to do this ("I know better").