Recently, I encountered a problem when I tried to declare init
accessor for a property in C# with enabled nullable reference types.
I declared my public class MyClass
with a string Name
property that should not be null.
And, because I want to be fancy and use modern C#, I decided to use init
accessor syntax:
public class MyClass
{
public required string Name { get; init; }
}
There is one thing that concerns me about the code above. I really like when types can strictly guarantee that they are in a consistent state no matter how they are used by the calling code. I believe this is a sign of a good design. Usually, I do checks in the constructor like this:
public class MyClass
{
public string Name { get; }
public MyClass(string name)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
Name = name;
}
}
However, in with init
accessor the calling code can easily create an inconsistent MyClass
instance with a code like this:
var instance = new MyClass { Name = null };
I know that the nullable ref types analysis should help against this issue, but still it is not a 100% guarantee. What if MyClass
is created in the context where the analysis is disabled? What if its warnings are ignored?
This is why I decided to add a check to the init
accessor. First of all, I found out that I can’t do this with C# auto properties. I need a backing field. So, my code transformed into this:
public class MyClass
{
// compiler warning that non nullable _name field is not initialized
private readonly string _name;
public required string Name
{
get => _name;
init
{
ArgumentException.ThrowIfNullOrWhiteSpace(value);
_name = value;
}
}
}
There is more boilerplate in this version. But what’s even worse is that now the code has a CS8618
compiler warning about non nullable _name
field left uninitialized by the default constructor. And there is no good workaround for it.
I can’t mark the _name
field as required
, because the field is private
. Even though I still can mark the Name
property as required
, the compiler refuses to see the relationship between the backing field _name
and property Name
. The only options I found are either to suppress the warning with pragma directive or to use the null forgiving operator like this:
private readonly string _name = null!;
I don’t like both of them. Is there a simpler and more concise way to have a validation logic (like null checks) with init property accessors.
UPDATE:
After some discussion in the comments I have updated the code to separate the approach with input checks in constructor and focus on the code with init
accessor. I would like to know if there is a simple way to make input validations with init
accessors approach and without compiler warnings.
21