Required non-initialized non-nullable properties
With the introduction of nullable reference types, null-forgiving operator is often used to avoid compiler warnings on non-nullable properties, which are initialized by library code. The required
modifier can be a good alternative to that.
CsvHelper is a library where you can encounter such an issue. When following the documentation, you will create a simple class with a property for each column in the CSV file. For example:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
With nullable reference types enabled, this will result in a compiler warning:
The warning can be fixed by initializing the property with a non-null value. Or alternatively, with a null value followed by the null-forgiving operator:
public class Person
{
public string FirstName { get; set; } = null!;
public string LastName { get; set; } = null!;
}
This way, the property value is still null
, but the compiler doesn't complain anymore.
That's perfectly fine, when the class instances are generated by CsvHelper reading a CSV file. If the corresponding column is present in the file, The library will assign a non-null value to the property. If there's no corresponding column in the file, the library will throw an exception.
However, when creating class instances from code instead, the compiler will not warn you if you don't initialize the properties. And your instance will have non-nullable properties with a null
value. You're using nullable reference types to avoid this exact scenario.
You could create a constructor with parameters for all non-nullable properties (or use a record with a primary constructor) instead of using the null-forgiving operator. CsvHelper supports this (although it can make configuring the mapping with attributes more tricky). But not all libraries do.
C# 11 introduced the required
modifier, which allows you to not initialize all the non-nullable properties:
public class Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
}
This also works perfectly fine with CsvHelper (and similar libraries). But in contrast to using the null-forgiving operator, you'll get a compiler error if you fail to initialize such properties when instantiating the class from code:
You can find a small sample project in my GitHub repository. The final commit shows the use of the required
modifier. The one before that uses null-forgiving operator instead.
While null-forgiving operator can be used to resolve nullable warnings, it's usually a bad idea that can lead to hidden bugs in the code. In this post, I've shown how the required
modifier can sometimes be used instead, resulting in safer code.