30

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 ?

8
  • 5
    @PanagiotisKanavos: In this case, it's both: OP wants the code to crash early, when UserService is initialized, rather than late, when uow is accessed. Commented Nov 27, 2023 at 12:04
  • 11
    Be aware that if you use a primary constructor, the field that it introduces is NOT READONLY. Which is quite annoying. See Nick Chapsas's video about it Commented Nov 27, 2023 at 12:05
  • 3
    @MatthewWatson: Indeed. Fortunately, the C# team thinks the same and has announced that they want to fix this in the next version (probably with an optional readonly modifier for PC parameters). Commented Nov 27, 2023 at 12:09
  • 1
    Wait what? You're passing a unit of work to the constructor of a service?? That doens't seem right. Even passing the database context is not a great idea, as I will block the database. But a unit of work should be severly limited in lifetime. Commented Nov 29, 2023 at 16:24
  • 1
    @RandRandom: That feature was only available during preview of C#11 and has been removed. Commented Dec 4, 2023 at 9:46

4 Answers 4

34

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;
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

If you don't use the helper method, private readonly IUnitOfWork _uow = uow ?? throw new ArgumentNullException(nameof(uow)); is a bit shorter. 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 additional name: Yes, private ... uow = uow.IsNotNull(); looks strange, but it works.
Naming the field and the PC parameter the same has the additional advantage that your mutable PC parameter is now shadowed by an immutable field.
Thank you @Heinzi , updated! As for the field name - followed the convention from OP. Added as a remark to the answer.
this pattern is also conducive to source generation ;)
2

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.

1 Comment

agreed. If you can't trust your dependency injection, then you'll have a big problem anyhow.
1

You can use Metalama to check for nulls in parameter values.

  1. Add the Metalama.Patterns.Contracts NuGet package.

  2. Add [NotNull] attribute to the checked parameter.

public class UserService([NotNull] IUnitOfWork uow) : IUserService
{
}

That's it.

Doc: https://doc.metalama.net/patterns/contracts/adding-contracts

Comments

0

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...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.