'Avoid CS8618 warning when initializing mutable non nullable property with argument validation
I have a question regarding nullable reference type system available since C# 8.
Suppose we have a C# domain model class with a mutable reference type property like below:
public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
So far no problem. But consider real world scenario, I often want to check the validity of the property as it's a public mutable property and I have to make sure the model invariant whenever property is changed.
public class Person
{
private string _name;
public string Name
{
get => _name;
set => _name =
value ?? throw new ArgumentNullException("Name is required.");
}
public Person(string name)
{
Name = name;
}
}
Then compiler generates CS8618
warning, basically saying:
Non nullable field _name is not initialized. Consider declare the field as nullable type.
So every time I encounter the warning I have to enclose the constructor with the following pragma directive.
#pragma warning disable CS8618
public Person(string name)
{
Name = name;
}
#pragma warning restore CS8618
But I think it's redundant and tedious to do it always. Am I misusing something or is there better way to write such property without warning?
Of course I can change the property type to string?
as compiler suggests but notionally it's not acceptable as a solution as Person should always have non null name and we want to explicit about such invariant condition in domain class.
Another solution I considered is to drop the argument validation logic and just relying on the nullable compiler warning, but it's not always possible (I mean often validation other than null check is also required.), it's just warning anyway in regular project settings, so I don't think it's a good solution.
Solution 1:[1]
For now you can avoid this warning by initializing a _name
field using default value with null-forgiving operator !
, like
private string _name = default!;
or
private string _name = null!;
There is also an open GitHub issue for that.
You can also declare the _name
as string?
and specify that return value of Name
property can't be null
(even if string?
type allows it), using NotNull
attribute
private string? _name;
[NotNull]
public string? Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException("Name is required.");
}
It should be fine, otherwise compiler shows you a warning before validation logic will take place in a setter
set => _name = value ?? throw new ArgumentNullException("Name is required.");
Consider the following code
var person = new Person(null);
In this case you'll get
warning CS8625: Cannot convert null literal to non-nullable reference type.
before ArgumentNullException
will be thrown.
If you set <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
or treat CS8625
warning as error, your exception won't be thrown
Solution 2:[2]
You can disable the rule by creating an .editorconfig
file (with the attached code) in the root of your project. It does not solve it but it will no longer show the warning
[*.cs]
# CS8618: Non nullable field _name is not initialized. Consider declare the field as nullable type
dotnet_diagnostic.CS8618.severity = none
Solution 3:[3]
You can now apply MemberNotNull
attribute on the setter to let the C# compiler know that non-nullability condition for the _name
field is being maintained by that method.
using System.Diagnostics.CodeAnalysis;
public class Person
{
private string _name;
public string Name
{
get => _name;
[MemberNotNull(nameof(_name))]
set => _name = value ?? throw new ArgumentNullException("Name is required.");
}
public Person(string name)
{
Name = name;
}
}
Solution 4:[4]
Based on this:
Warnings for initialized fields Q: Why are warnings reported for fields that are initialized indirectly by the constructor, or outside the constructor?
A: The compiler recognizes fields assigned explicitly in the current constructor only, and warns for other fields declared as non-nullable. That ignores other ways fields may be initialized such as factory methods, helper methods, property setters, and object initializers. We will investigate recognizing common initialization patterns to avoid unnecessary warnings.
So with that being said, for now, moving the assignment directly into the constructor, is the only possible way. And for sure, using the pragma directive seems fine for this IMO.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | r0- |
Solution 3 | |
Solution 4 |