Consider an example:
class Test {
string S { get; set; }
public Test() {
Init();
}
private void Init() {
S = "hello";
}
}
Using nullable C# project feature, this sample will trigger a compiler warning:
Warning CS8618 Non-nullable property 'S' must contain a non-null value when exiting the constructor. Consider declaring the property as nullable.
However, the property DOES contain non-null value when exiting the constructor, it's just not set directly in the constructor, but indirectly in a method that is unconditionally called from the constructor.
This example shows clearly that there is no possibility for the S
property to ever be null. When an instance of the Test
class is created, the Init()
method is called unconditionally, so the S
property is ALWAYS set to "hello".
Of course, this warning can be suppressed in code, but this looks just fugly.
Is it a better way to tell the compiler I really did set the S
property to a non-null value elsewhere?
BTW, if you really wonder WHY to set values in the constructor indirectly, let's consider there is another derived property D
of Derived
type. To create an instance of Derived
the string must be parsed first and we don't want to parse the string each time we read the D
property.
So, the more realistic code would look more like this:
class Test {
public string S {
get => _S;
set => D = new Derived(_S = value);
}
public Derived D { get; private set; }
public Test(string s) => D = new Derived(_S = s);
private string _S;
}
As you can see, both S
and D
are set to non-null values when exiting the constructor.
Yet the code still triggers the compiler warning CS8618.
_S
is set in the constructor.S
itself doesn't need to be set as its getter returns something non-nullable. – FeazeInit
method isvirtual
and a derived type simply does not initializeS
? Too many scenarios IMO and decorating with attributes to tell compiler "I know what I am doing" is better. – ExplicativeInit
would trigger the compiler error instead. In case of method hiding - the original Init would be called anyway. But you made me curious - is it possible to break the code somehow with usingMemberNotNullAttribute
? I guess the attribute exists for the CA performance reason, just as you've mentioned. – Interestprotected virtual void Init()
and override it with a derived type. If you do not callbase.Init()
, you receive the same warning. About compiler non-virtualInit
, performance would be one reason IMO, the other is,Init
may call further more methodsInitS
callingInternalInitS
down the line and it's just not worth it. – Explicative