Null forgiving operator

Suppressing compiler null reference warnings with the null forgiving operator

Home DailyDrop

Daily Knowledge Drop

C# contains a unary, postfix, null-forgiving (or null-suppression) operator !, which can be used to declare that an expression of a reference type is not null.

The null-forgiving operator is used in a nullable enabled context, has no effect at run time, and only affects the compiler's static flow analysis by changing the null state of the expression in question.

Effectively, the operator informs the compiler that an expression which it has determined to be null, is in fact not null and no diagnostic alert (a warning by default) needs to be raised.


Usage

Unit testing

A practical use of the ! null-forgiving operator, is when performing unit tests.

Consider the following class, which has an Artist property, forced to be non-null by the constructor:

public class Song
{
    public string Artist { get; }

    public Song(string artist)
    {
        Artist = artist ?? throw new ArgumentNullException(nameof(artist));
    }
}

When unit testing the class, one would want to test all possible combinations of Song instantiation - with and without a valid artist value:

// Instantiation to test created successfully
var song = new Song("Foo Fighters");

// Instantiation to test exception is thrown
var exceptionSong = new Song(null);

The issue with the above, is at compile time, the compiler sees the null being passed to the constructor and raises the following warning:

Cannot convert null literal to non-nullable reference type.

In this case, we want to pass null as part of a unit test to determine the correct behavior is seen. The null-forgiving operator can be used to effectively tell the compiler, that the null expression is not null, and the compiler does not need to treat is as such (and thus no warning is generated)

Updating the code to the following, will remove the warning:

// Instantiation to test created successfully
var song = new Song("Foo Fighters");

// Instantiation to test exception is thrown
// Null-forgiving operator added
var exceptionSong = new Song(null!);

Manual check

Another practical use of the null-forgiving operator, is in situations when the compiler has incorrectly determined that an expression could possibly be null.

Consider the below method to validate an instance of Song:

static bool IsValid(Song? song)
    => song is not null && song.Artist is not null;

And the usage:

// get a nullable Song instance 
// from a source
Song? s = GetSong();

// validate the song and output if valid
if (IsValid(s))
{
    Console.WriteLine($"Song by Artist `{s.Artist}` is valid");
}

The above code generates the following warning on the s.Artist usage:

Dereference of a possibly null reference.

This warning is due to the fact, that based on the data types, s (of type Song?) could contain a null value, in which case s.Artist would cause an exception. However, the IsValid method is performing checks to ensure that s.Artist can never be invoked when s is null - in this use case the warning is incorrect.

The null-forgiving operator can be used again to tell the compiler, that the s expression in s.Artist can be treated as never being null.

Updating the code to the following, will remove the warning:

// get a nullable Song instance
Song? s = GetSong();

if (IsValid(s))
{
    // null-forgiving operator added
    Console.WriteLine($"Song by Artist `{s!.Artist}` is valid");
}

Notes

While not an operator for every day use for most applications, the null-forgiving operator can be very useful in resolving certain inaccurate warnings. However take care when using the operator, and only implement when sure that the compiler is incorrect. While the code itself will not throw any exceptions because of the operator usage (it's ignored at runtime), the code could throw exceptions due to the the expression in question being null - which the compiler was trying to warn about (before being manually overwritten with the operator)


References

! (null-forgiving) operator


Daily Drop 125: 27-07-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net null operator