Search code examples
c#validationnameof

Null-check multiple parameters and throw an exception with their name


I would like to validate multiple parameters and throw an ArgumentNullException if any of them are null. For the sake of argument, let's assume I've got this:

public void DoSomething(SomeClass param1, SomeClass param2, SomeClass param3);

Of course, I could do:

if (param1 == null)
    throw new ArgumentNullException(nameof(param1));
if (param2 == null)
    throw new ArgumentNullException(nameof(param2));
if (param3 == null)
    throw new ArgumentNullException(nameof(param3));

But it's not particularly pretty, especially if it's a recurring check in throughout the application. So, I thought I would do this:

public static class ValidationExtensions
{
    public static void NullCheck<T>(this T subject)
    {
        if (T == null)
            throw new ArgumentNullException();
    }
}

// ...

param1.NullCheck();
param2.NullCheck();
param3.NullCheck();

But this way I lose the nameof. I can't do nameof(subject) as that's meaningless.

Of course, this is an option:

public static class ValidationExtensions
{
    public static void NullCheck<T>(this T subject, string parameterName)
    {
        if (T == null)
            throw new ArgumentNullException(parameterName);
    }
}

// ...

param1.NullCheck(nameof(param1));
param2.NullCheck(nameof(param2));
param3.NullCheck(nameof(param3));

But it seems prone to error, with the repeated params... and, to be honest, just not pretty.

Is there a nice way of doing this? Ideally without using any external libraries.


Solution

  • The most succinct and maintainable solution would be what you have, or C#7 Throw Expression

    param1 = param1 ?? throw new ArgumentNullException(nameof(param1));
    

    You could use Expressions and some smarts, though i wouldn't recommend this, its smells and hides simple logic behind an abstraction and overhead. Additionally, it relies on unspecified behaviour that might change in the future

    However, all that aside, i give you Expressions

    public static class Validator
    {
       public static void Validate<T>(Expression<Func<string, T>> f)
       {
          var name = (f.Body as MemberExpression).Member.Name;
          if(f.Compile().Invoke(name) == null)
             throw new ArgumentNullException(name);    
       }
    }
    

    The reason this works, is because the compiler generates a class for the lambda expression (a closure) and the local variable becomes a property Member.Name, which means it should also work for properties too (untested)

    Usage

    public static void Test(string param1, string param2)
    {
       Validator.Validate(x => param1);
    }
    
    public static void Main()
    {
       Test(null,"asdf");
    }
    

    Output

    Value cannot be null. Parameter name: param1

    Note : Truthfully i haven't thought about this too much or tested it more than a couple of use cases, it may or may not work, so i am not responsible for the people you injure with this code