216

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?

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
isxaker
  • 8,446
  • 12
  • 60
  • 87
  • 41
    `!` is the [null-forgiving operator](https://github.com/dotnet/csharplang/blob/master/proposals/nullable-reference-types-specification.md#the-null-forgiving-operator), telling the compiler that, even though it normally wouldn't allow it, it should look the other way and allow it anyway, because we know better. `null!` itself has little practical use, as it all but negates the usefulness of nullable reference types. It's more useful when you know an expression can't be `null`, but the compiler doesn't. – Jeroen Mostert Feb 16 '19 at 14:58
  • 1
    @JeroenMostert so smth like force? I mean even if it is unusual lets do that forced. – isxaker Feb 16 '19 at 15:06
  • 5
    Yes, except it's more than unusual -- because `string`, under the new rules, is not a nullable reference type, and so should never be `null`. Assigning `null!` effectively says "I know this should never be `null`, but guess what, I'm doing it anyway". There's almost no program where that would make sense -- the only reason to do it would be because you know you're going to assign a non-`null` value before anyone could get a `NullReferenceException`, and want to signal that you haven't forgotten to assign it. Possible, but unlikely, so not very good as an example. – Jeroen Mostert Feb 16 '19 at 15:26
  • 7
    @JeroenMostert I have found the null! to be useful in unit tests. Just because a reference shouldn't be null doesn't necessarily mean it won't be, so sometimes testing for the right behavior in that situation is appropriate. – Jesse Jan 14 '20 at 19:00
  • 1
    @JeroenMostert It's also useful for game engines like Godot where you commonly defer reference field initialization for nodes and resources until the _Ready() callback method, instead of the constructor. It effectively shuts up the compiler. – knightofiam Mar 14 '21 at 23:17
  • 1
    From comments, Kotlin lateinit var equivalent. – Rifat Oct 20 '22 at 02:11

6 Answers6

380

TL;DR

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".

Basically, null! applies the ! operator to the value null. This overrides the nullability of the value null to non-nullable, telling the compiler that null is a "non-null" type.


Typical usage

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.

Technical Explanation

The groundwork that you will need to understand what null! means.

Null Safety

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 NullReferenceExceptions by forcing you to make a decision and enforcing it.

How it works

There are 2 states a variable can be in - when talking about null-safety.

  • Nullable - Can be null.
  • Non-Nullable - Cannot be null.

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 that you define with these operators to ensure null-safety.

Examples

? Operator usage.

This operator tells the compiler that a variable can hold a null value. It is used when defining variables.

  • Nullable string? x;

    • x is a reference type - So by default non-nullable.
    • We apply the ? 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 object? is basically just syntactic sugar for System.Nullable<object>

! 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. It is used when accessing variables.

string x;
string? y;
  • x = y
    • Illegal! Warning: "y" may be null
    • The left side of the assignment is non-nullable but the right side is nullable.
    • So it does not work, since it is semantically incorrect
  • x = y!
    • Legal!
    • y is a reference type with the ? type modifier applied so it is nullable if not proven otherwise.
    • We apply ! to y which overrides its nullability settings to make it non-nullable
    • The right and left side of the assignment are non-nullable. Which is semantically correct.

WARNING The ! operator only turns off the compiler-checks at a type-system level - At runtime, the value may still be null.

Use carefully!

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.

Reasoning

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 !.

Why does this operator exist then?

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.

  • In some (edge) cases, the compiler is not able to detect that a nullable value is actually non-nullable.
  • Easier legacy code-base migration.
  • In some cases, you just don't care if something becomes null.
  • When working with Unit-tests you may want to check the behavior of code when a null comes through.

Ok!? But what does 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!;
Patrick Hollweck
  • 5,604
  • 2
  • 19
  • 34
  • 7
    @canbax Nullabilty checks are only supported by c#8 and above – Patrick Hollweck Feb 20 '19 at 06:42
  • 3
    @canbax `a string could be null` not any more. I mean if you use `c# 8` and enable `NullableReferenceTypes` feature. VS immediately gives you a warning if you try assign `nul`l to `string`. But from the other hand you're able to introduce nullable string(`string? s = null`). No warning in that case. – isxaker Feb 20 '19 at 10:10
  • 4
    "You should try to never use the ! Null-Forgiving-Operator. It negates the effects of null-safety you get guaranteed by the compiler." How are you going to write unit tests for argument validation? That's my most frequent use of ! in Noda Time. – Jon Skeet Feb 23 '19 at 13:18
  • 4
    @JonSkeet I'd consider unit-tests a special case here - Since you intentionally work with nulls. With "You should try to never use" I was referring to "normal" code - Where using this operator can yield unexpecting results. Aka something became null even though you declared it non-nullable. I wanted to tell people not to slap a `!` everywhere - just to get rid of the warning. Because then you dont get the benefits of null-safety - Will maybe edit later to make more clear – Patrick Hollweck Feb 23 '19 at 16:06
  • 1
    I would *definitely* recommend editing later - currently anyone who is widely using the null-forgiving operator in their tests could easily feel bad about it given your current text, which doesn't give any indication of limitations to the advice. I think "never" is likely to be unachievable in several code bases - for example, in Noda Time we have an invariant in parse results that means I never *expose* a null value, but I still end up with one where the parse itself has failed. I think "try hard to avoid" has a more positive tone. Just my opinion though. – Jon Skeet Feb 23 '19 at 16:21
  • @JonSkeet Yeah, you are right - One should definitely not feel bad about using this operator in a test context. I replaced "never" with "try to avoid" and added unit tests to the "exception" list. You really make me overthink my whole post. – Patrick Hollweck Feb 23 '19 at 16:31
  • Still not 100% sure from this answer why I'd use `string x = null!;`. My quick guess is b/c you know you're going to overwrite that null value in the same code where the var was initialized. But then why initialize at all? Does a non-nullable string initialize to `string.Empty` by default and that's bad? (I can't think of a use case in this situation (you are going to write a non-null value "soon") where it would be.) But if there's a use case; why not use a nullable string if checking against null is your goal? Skeet's test case -- null where there shouldn't be -- is the only that make sense. – ruffin May 27 '21 at 14:21
  • 1
    @ruffin There is very little reason to use `null!` the example given in the question also does *not* demonstrate a real use-case imo. There are a few situations where you would want to use `!` tho (See the "Why does this operation exist then" Section). This post is more about giving you the tools to understand this syntax/feature. This is the kind of feature where you "just know" you need it when encountering something specific while coding... A third of the post is also telling you to use this feature very carefully since like you said - using this may be very "out of the ordinary" – Patrick Hollweck May 27 '21 at 14:31
  • @ruffin `string x = null!;` as a variable is not used very often that I'm aware of, but as a field it is important in cases where you want to treat the field as not-nullable, but can't initialize it right away even though you know it will/should be initialized before you start to make use of it. (another option is to make it nullable anyway and just always null-check if before every usage, which can also be a valid approach.) (and another option can be to use properties or methods like GetXOrThrow or XOrDefault to make the null-checking more straightforward.) – Dave Cousineau May 27 '21 at 16:02
  • @DaveCousineau But see above: "_But then why initialize at all? Does a non-nullable string initialize to `string.Empty` by default and that's bad?_" I mean, it's got _some_ value when it's declared; what is that and why is `!null` better if you're **immediately assigning**? As Patrick clarified, there doesn't appear to be much practical use for `!null` at all -- except in cases, like Skeet's, where you're testing [horn of a rabbit](http://rywiki.tsadra.org/index.php/ri_bong_rwa#tab=null) situations. – ruffin May 27 '21 at 16:27
  • @ruffin as a field, `string x;` will default to `null` and you will get a warning saying that the non-nullable field will have a null default value. `string x = null!;` removes this warning. (`!` does two things: changes a nullable type to a non-nullable type; and also suppresses nullability warnings in general for that expression (not 100% sure of the range-of-effect of the suppression).) – Dave Cousineau May 27 '21 at 16:36
  • @ruffin It is worth noting that `null!` is **not** what the null-forgiving operator was primarily designed for. It is merely an "artifact", something that exists for completeness. It is mainly intended to be used on nullable variables. Someone that knows the nullable-reference types feature will most likely be able to figure out what `null!` means rather quickly, This question was posted when the feature was still new, therefore my answer became a sort of introduction to the feature. But I find many people find it and think `null!` is something that is used alot, when in reality it's not. – Patrick Hollweck May 27 '21 at 18:55
  • @PatrickHollweck it depends what you're doing. I use `null!` all the time, and almost never use it in other kinds of expressions. `null!` is extremely useful (or even completely necessary) when initializing variables/properties implicitly. – Dave Cousineau May 31 '21 at 16:30
  • You have mentioned that `x is a reference type - So by default non-nullable.` - reference type by default have null value - isnt it? – variable Jan 14 '22 at 07:31
  • Also, suppose if I initialize a property with `public string Prop1 { get; set; } = null!;` in order to get rid of the warning, then in my code when I initialize this class and set this `instance1.Prop1 = null;`, it doesn't give me any warning. Does `=null!` at property level hide that warning also? – variable Jan 14 '22 at 08:05
  • @variable Yeah a reference type will default to null if not assigned. But the compiler enforces the assignment of non-nullable variables. I cannot reproduce what you have stated in your second comment. I created this repl to demonstrate the behaviour: https://replit.com/@PatrickHollweck/so-null-assign#main.cs Take a look if you want :) – Patrick Hollweck Jan 17 '22 at 09:38
  • @PatrickHollweck What would then be the general consensus for using `= null!` when defining properties on an object? Those properties should never be assigned null, but I don't want to have to declare them all as nullable just to get rid of warnings? Or is that the way to go....but then I would do a nastier thing of using `!` when referencing those properties. – Heinrich Apr 03 '22 at 23:38
  • @PatrickHollweck Are you sure this is correct?: "Nice to know: Using string? is syntactic sugar for System.Nullable"? When I do ´string? s = null;´ the IL-Code will be IL_0000: ldnull IL_0001: stloc.0 // s – BennoDual May 14 '22 at 13:45
  • @Heinrich I don't understand your Question, can you elaborate? You have an object with optional properties that should also be not null, and you don't know how to initialize them? – Patrick Hollweck Jun 08 '22 at 14:42
  • @BennoDual I know very little about .NET IL so I don't understand what you are trying to tell me. But from what I researched "ldnull" initializes a property with null which is exactly what you are doing in your code sample. I am fairly sure that null-checking does change the emitted IL much or at all. `System.Nullable` is also an opaque type which you cannot interact with directly when generated by the compiler, since it does automatic boxing/unboxing – Patrick Hollweck Jun 08 '22 at 14:52
  • @PatrickHollweck string is a nullable (reference) type. string? is in the "Null safty" environment only a information for static code analysis that it accept null without warning. the Nullable-Struct accepts for the generic type parameter only valuetypes. When you use int? this will be converted to System.Nullable because int is a valuetype and valuetype do not accept null. – BennoDual Jun 09 '22 at 17:11
  • 1
    @PatrickHollweck When you define a POCO with a property you can have do it into ways `class POCO { public string Name { get; set; } }` vs `class POCO { public string? Name { get; set; } }`. Both of these have semantically different meanings. Now I don't want to use the latter when I know that `Name` won't actually be null when it gets used. But using the the initial causes a spamming of warnings. Which you can suppress using the null suppressing statement i.e. the initial becomes `class POCO { public string Name {get; set; } = null! }`. So I was referring to ` = null!` in that context. – Heinrich Jun 10 '22 at 00:06
  • 1
    @Heinrich OK so you have an object with a property that semantically should never be null so you don't mark it nullable. But you get a warning from the compiler because you don't initialize the property. Which you have to do, because if you don't the value ends up as null. I think we have a A/B Problem here. The answer is: Give the class a constructor and don't create the object until you have a value to initialize the property with. Otherwise it does not make sense to make the property non-nullable. You can't have both. = null! Should not be used for this... – Patrick Hollweck Jun 11 '22 at 07:33
  • This explanation is peace of art. much more better than the Microsoft said. So many thanks @PatrickHollweck – XAMT Dec 14 '22 at 07:08
  • Here: `Nice to know: Using object? is basically just syntactic sugar for System.Nullable`, what do you mean by "syntactic sugar"? Can you explain any further? May be that sentence explanation is not that sweet, if you know what I mean. – carloswm85 Apr 25 '23 at 16:32
  • @carloswm85 [Nothing a quick google search cannot explain](https://softwareengineering.stackexchange.com/a/194010/258140) – Patrick Hollweck Apr 26 '23 at 13:01
  • I know that, but Google does not provide a contextual explanation from inside this answer. Googling the whole sentence is just a copy + paste move. I can't put together nor relate that sentence inside the context of the section "`?` Operator usage" (and maybe the whole answer). – carloswm85 Apr 26 '23 at 14:39
  • @carloswm85 Someone who knows what "syntax sugar" is, can reasonably be expected to understand that sentence. I cannot explain the whole of programming in this answer, it is long enough already. If you are genuinely confused: Anytime you write `TYPE?` the compiler turns it into `System.Nullable` – Patrick Hollweck Apr 27 '23 at 11:40
24

null! is used to assign null to non-nullable variables, which is a way of promising that the variable won't be null when it is actually used.

I'd use null! in a Visual Studio extension, where properties are initialized by MEF via reflection:

[Import] // Set by MEF
VSImports vs = null!;
[Import] // Set by MEF
IClassificationTypeRegistryService classificationRegistry = null!; 

(I hate how variables magically get values in this system, but it is what it is.)

I also use it in unit tests to mark variables initialized by a setup method:

public class MyUnitTests
{
    IDatabaseRepository _repo = null!;

    [OneTimeSetUp]
    public void PrepareTestDatabase()
    {
        ...
        _repo = ...
        ...
    }
}

If you don't use null! in such cases, you'll have to use an exclamation mark every single time you read the variable, which would be a hassle without benefit.

Note: cases where null! is a good idea are fairly rare. I treat it as somewhat of a last resort.

Qwertie
  • 16,354
  • 20
  • 105
  • 148
  • 5
    This is the only answer that actually explains why on Earth one might actually want to write `null!` in their source code. – user3840170 Jun 18 '21 at 09:08
  • 2
    No, it does not, In both of these cases, you are better of not assigning the variable at all. There is no reason to default initialize those members with null – Patrick Hollweck Jun 21 '21 at 07:12
  • 2
    @PatrickHollweck No, failure to assign a value causes warning CS8618 (or in struct constructors, error CS0843.) – Qwertie Jun 26 '21 at 03:24
  • No offense, but this is circular logic. Agree with Patrick. This also appears to expose a massive flaw in c# 8 reasoning, now. By defaulting ref types to all non-nullable, Microsoft has stripped out an important state of initialized objects, which was null. If we are forced to use this new logic now, you are better off creating a default value for every type than the old way using these phony ignored nulls. Otherwise, this looks silly... – Stokely Aug 10 '21 at 19:20
  • 1
    There is no circle in the logic. – Qwertie Aug 12 '21 at 23:04
  • A much better explanation than the accepted wall of text that doesn't explain what you mentioned in your very first sentence - it's a *promise* that the value won't be null. That's what confused me in my own code - I have a non-nullable property that I initialize in a method that I call in the constructor but the compiler complains that I'm not initializing it. Rider conveniently suggested to use `null!` and now it makes sense why it would suggest that. – Andris Sep 02 '23 at 18:25
19

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:

  • if you mark the field as nullable, the compiler is happy, but you have to un-necessarily check for null when you use the field,
  • if you leave the field as non-nullable, the compiler will complain that it is not initialized by the constructors (you can suppress that with 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 of null! to initialize a field covers up the fact that a null is slipping in. Some unsuspecting code can receive a null 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":

if (CheckEverythingIsReady())
{
   // you know that `field` is non-null, but the compiler doesn't. The suppression can help
   UseNonNullValueFromField(this.field!);
}

Again, you must be confident of your code's invariant to do this ("I know better").

Julien Couvreur
  • 4,473
  • 1
  • 30
  • 40
3

I don't believe this question can be discussed without specific C# version being used.

public string RpyDescription { get; set; } = null!;

This is common in .NET 6 Core ... in fact it's required if you want to describe a string as not being nullable. One reason this exists is when one is working with SQL databases where a field can be set to "Allow Null". Even more so when working with JSON structures that accept null. EF needs to know.

Reference Type (Heap - pointer to memory location where the data is stored) of Value Type (Stack - memory location where data is stored).

.NET 6 (C#10) enables nullable context for the project templates by default (prior to this nullable context is disabled by default).

In EF/Core, it's very important to understand relationship between database null and model/entities null.

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
Rob
  • 51
  • 1
  • 1
  • 8
  • you are right, I would actually go further, whatever API, Database, GUI, JSON structure. You need to lay out for other programmers the "Null Rules", for example what type of wheels does a car have. If we allowed this to be null when our Database needs this to be a not null value, or the JSON we are creating needs to be a not null value. This would be far inferior to what we have now, where we can specifically state the null rules. edge cases, should be dealt with by creating some reasonable default value and making a comment about why this object is being instantiated without that value. – jdmneon Oct 24 '22 at 23:08
  • Why not `public string RpyDescription { get; set; } = "";`? – Qwertie Nov 01 '22 at 20:37
  • 1
    @Qwertie basically (when using EF) "string? Property" signifies an optional property, "string Property = string.Empty;" is a mandatory property with a default value of string.Empty and "string Property = null!;" is a mandatory property without a default value, i.e. the property must be explicitly set before the entity can be written to the database. – hhravn Dec 09 '22 at 13:02
  • @Qwertie In my job (banking) assign empty string as default value is very super extremely very dangerous. Did I say very twice? – Persk Jan 26 '23 at 03:19
1

One valid use case is when the values of an object are assigned at runtime, such as with IOption property mappings which are required.

You can either:

  1. Mark the properties as nullable and deal with null checks everywhere.
  2. Set a default value of "" which will allow the configuration property to be undefined, and then check for string.IsNullOrEmpty() whenever it's consumed.
  3. Set the default value as null!, which disables the IDE warning, and will force the app to exit on configuration startup if the property isn't set.
Kristian Williams
  • 2,275
  • 22
  • 25
-1

This question needs to be updated, in C# 10 in object relational mapping, this operator, combined with the ? operator is critical, this is a way to tell other coders on the project, future coders, and remind yourself how the data is going to end up being consumed and the null rules regarding that data.

public string Data { get; set; } = null!;
public string? Nullable { get; set; }

The beauty of this is that you don't have to go and look at the API docs(which you might not have), or go look through your database (which you might not even have access to). You already know by glancing at the class what the rules regarding null values are.

The downside is that if you instantiate the class, and don't have the information you need to instantiate the NOT NULL values, you will get strange default values that don't always make sense. First of all this doesn't happen nearly as commonly as people think and often comes from lazy programming. When you instantiate an instance of a class. You should calculating or assigning those NOT NULL properties. Right away. If you declare a car, and in your database or API cars have wheels, if you declare a car without assigning property values to the wheel.

Then did you truly instantiate the car in a meaningful way? You certainly didn't define the car the way the Database understands a car, it's a car with no wheels and it shouldn't be, doing this might be convenient but it goes against basic principles of object oriented programming.

are there exceptions for example perhaps the value can't be known at that point in time or until other events have transpired in these edge cases. Create a meaningful default value manually, if your default value hits the database you will know EXACTLY what's wrong

for people discussing unit tests why would you test the null case when the null case is impossible in the context of the object there is no need to have an understanding of how a car without wheels would behave for example. This is a silly, badly designed test.

I would go so far as to say that when it comes to strings this operator should be used the overwhelming majority of the time. When we declare int, or bool, or float, we have the understanding that these cannot be null unless we say so explicitly. Why in the world would you advocate for a different rule for strings!? the fact that we couldn't do this with strings previously was a design flaw.

jdmneon
  • 444
  • 7
  • 12
  • I'm also going to add that very often there is the assumption that the "Code First" model is being used. In real life, the data and the rules surrounding it, almost always exists before the application. The number of applications written Database First is severely underestimated. Writing Code First is great but very often it's impractical. You don't always have that kind of control over your database. – jdmneon Oct 24 '22 at 22:54