Best way to check for null parameters (Guard Clauses)

后端 未结 10 1804
北荒
北荒 2021-01-31 16:45

For example, you usually don\'t want parameters in a constructor to be null, so it\'s very normal to see some thing like

if (someArg == null)
{
    throw new Arg         


        
相关标签:
10条回答
  • 2021-01-31 17:27
    public static class Ensure
    {
        /// <summary>
        /// Ensures that the specified argument is not null.
        /// </summary>
        /// <param name="argumentName">Name of the argument.</param>
        /// <param name="argument">The argument.</param>
        [DebuggerStepThrough]
        [ContractAnnotation("halt <= argument:null")]        
        public static void ArgumentNotNull(object argument, [InvokerParameterName] string argumentName)
        {
            if (argument == null)
            {
                throw new ArgumentNullException(argumentName);
            }
        }
    }
    

    usage:

    // C# < 6
    public Constructor([NotNull] object foo)
    {
        Ensure.ArgumentNotNull(foo, "foo");
        ...
    }
    
    // C# >= 6
    public Constructor([NotNull] object bar)
    {
        Ensure.ArgumentNotNull(bar, nameof(bar));
        ...
    }
    

    The DebuggerStepThroughAttribute comes in quite handy so that in case of an excpetion while debugging (or when I attach the debugger after the exception occurred) I will not end up inside the ArgumentNotNull method but instead at the calling method where the null reference actually happend.

    I am using ReSharper Contract Annotations.

    • The ContractAnnotationAttribute makes sure that I never misspell the argument ("foo") and also renames it automatically if I rename the foo symbol.
    • The NotNullAttribute helps ReSharper with code analysis. So if I do new Constructor(null) if will get a warning from ReSharper that this will lead to an exception.
    • If you do not like to annotate your code directly, you can also do the same thing with external XML-files that you could deploy with your library and that users can optinally reference in their ReShaprer.
    0 讨论(0)
  • 2021-01-31 17:28

    You might try my Heleonix.Guard library, which provides guard functionality.

    You can write guard clauses like below:

    // C# 7.2+: Non-Trailing named arguments
    Throw.ArgumentNullException(when: param.IsNull(), nameof(param));
    
    // OR
    
    // Prior to C# 7.2: You can use a helper method 'When'
    Throw.ArgumentNullException(When(param.IsNull()), nameof(param));
    
    // OR
    Throw.ArgumentNullException(param == null, nameof(param));
    
    // OR
    Throw.ArgumentNullException(When (param == null), nameof(param));
    

    It provides throwing of many existing exceptions, and you can write custom extension methods for custom exceptions. Also, the library refers to the 'Heleonix.Extensions' library with predicative extensions like IsNull, IsNullOrEmptyOrWhitespace, IsLessThan and many more to check your arguments or variables against desired values. Unlike some other guard libraries with fluent interfaces, these extensions do not generate intermediate objects, and since implementation is really straightforward, they are performant.

    0 讨论(0)
  • 2021-01-31 17:29

    If you have too many parameters in your constructors, you'd better revise them, but that's another story.

    To decrease boilerplate validation code many guys write Guard utility classes like this:

    public static class Guard
    {
        public static void ThrowIfNull(object argumentValue, string argumentName)
        {
            if (argumentValue == null)
            {
                throw new ArgumentNullException(argumentName);
            }
        }
    
        // other validation methods
    }
    

    (You can add other validation methods that might be necessary to that Guard class).

    Thus it only takes one line of code to validate a parameter:

        private static void Foo(object obj)
        {
            Guard.ThrowIfNull(obj, "obj");
        }
    
    0 讨论(0)
  • 2021-01-31 17:35

    The simplest approach I've found is inspired by Dapper's use of anonymous types. I've written a Guard class that uses anonymous types to get name of properties. The guard itself is the following

        public class Guard
        {
            public static void ThrowIfNull(object param) 
            {
                var props = param.GetType().GetProperties();
                foreach (var prop in props) 
                {
                    var name = prop.Name;
                    var val = prop.GetValue(param, null);
    
                    _ = val ?? throw new ArgumentNullException(name);
                }
            }
        }
    

    The use is then

    ...
            public void Method(string someValue, string otherValue)
            {
                Guard.ThrowIfNull(new { someValue, otherValue }); 
            }
    ...
    

    When the ArgumentNullException is thrown and the correctly named property will be displayed.

    0 讨论(0)
提交回复
热议问题