Search code examples
c#constructorpropertiesconstructor-injectionnull-check

Ensuring required properties in a class are not null


Having previously worked mostly with private fields in my classes and passing required objects into the constructor, I'm experimenting with using public properties with the required modifier keyword introduced in C# version 11.

One of the drivers behind this is that where I set up my dependency injection, some of my classes have five or six required objects that need passing in, and I find the code to be much easier to read when using object initialisers rather constructor initialisation, where objects of the same type might go unnoticed if they're provided in the wrong order (and yes I know I can specify the parameter name with constructor initialisation, but the code becomes very verbose, very fast, which is exactly what I'm trying to avoid).

I've tried using a parameter-less constructor to perform null checks on these properties within the constructor, as follows...

public class SomeClass
{
    public required Person Person { get;init; }

    public SomeClass()
    {
        ArgumentNullException.ThrowIfNull(this.Person);
    }
}

But when I instantiate the class

var foo = new SomeClass()
{
    Person = new Person()
};

I receive an exception from the null check in the constructor, so it's obvious that the constructor is called prior to the properties being set, which makes sense, but I'm now left thinking that if I must perform null checks on properties, should I steer clear of required properties and deal with the less aesthetically pleasing object initialisation code in other ways?

I could implement IValidatableObject, but that feels like a nasty code smell having to call a method for something I would otherwise handle with constructor initialization.


Solution

  • You can switch from auto-implemented properties to properties with backing field:

    public class SomeClass
    {
        private readonly Person _person; 
    
        public required Person Person
        {
            get => _person;
            [MemberNotNull(nameof(_person))]
            init
            {
                ArgumentNullException.ThrowIfNull(value);
                _person = value;
            }
        }
    }
    

    The constructor is invoked before the init expression (as with object initializers in general), so the actual value for property will be set after check performed in the constructor. Check out the decompilation @sharplab.io.