Null checking with primary constructor in C# 12

2.2k views Asked by At

I using C# 12. In C# 12 I can use primary constructor:

public class UserService(IUnitOfWork uow) : IUserService
{
}

Before C# 12 I used null checking for items that I inject in constructor:

public class UserService : IUserService
{
    private readonly IUnitOfWork _uow;

    public UserService(IUnitOfWork uow)
    {
        ArgumentNullException.ThrowIfNull(uow);
        _uow = uow;
    }
}

Now how can I do null checking in C# 12 ?
Is it need to use fail fast with primary constructor ?

3

There are 3 answers

3
Guru Stron On BEST ANSWER

As far as I know if you want to switch to primary constructors one of the easiest options would be to introduce field/property:

public class UserService(IUnitOfWork uow) : IUserService
{
    private readonly IUnitOfWork _uow = uow 
         ?? throw new ArgumentNullException(nameof(uow));
}

Note that you can also name the field the same as your constructor parameter (_uow -> uow), if you don't want to clutter your class with an extra name (as suggested by Heinzi) which has additional benefit of shadowing the mutable primary ctor parameter by an immutable field.

You can also encapsulate the logic into helper method. Something along these lines:

public class UserService(IUnitOfWork uow) : IUserService
{
    private readonly IUnitOfWork uow = uow.IsNotNull();
}

public static class Check
{
    [return:NotNull]
    public static T IsNotNull<T>(this T t,
         [CallerArgumentExpression("t")] string? paramName = null) where T : class
    {
        ArgumentNullException.ThrowIfNull(t, paramName);
        return t;
    }
}
1
Arthur On

Opinionated: for this example I would omit the null check. It adds code (a liability), at the expense of clutter and distraction. I don't see the value because if uow is null things will blow up quickly in UserService tests, the cause will be obvious, and be solved (probably) by registering IUnitOfWork.

0
undefined On

You can define properties with private setters and perform null checks in these setters.

public class UserService : IUserService
{
    private IUnitOfWork _uow;

    public IUnitOfWork Uow
    {
        get => _uow;
        private set => _uow = value ?? throw new ArgumentNullException(nameof(value));
    }

    public UserService(IUnitOfWork uow) => Uow = uow;

    // Other methods...
}

or you could use even try using Constructor Chaining so where the primary constructor calls a private constructor that performs the null checks.


public class UserService : IUserService
{
    private readonly IUnitOfWork _uow;

    public UserService(IUnitOfWork uow) : this(uow, true) { }

    private UserService(IUnitOfWork uow, bool checkNull)
    {
        if (checkNull)
        {
            ArgumentNullException.ThrowIfNull(uow, nameof(uow));
        }

        _uow = uow;
    }

    // Other methods...
}