Лучший способ проверить нулевые параметры (Guard Clauses)

Например, вы обычно не хотите, чтобы параметры в конструкторе были равны нулю, поэтому очень нормально видеть что-то вроде

if (someArg == null)
{
    throw new ArgumentNullException(nameof(someArg));
}

if (otherArg == null)
{
    throw new ArgumentNullException(nameof(otherArg));
}

Это немного загромождает код.

Есть ли способ проверить аргумент списка аргументов лучше этого?

Что-то вроде "проверьте все аргументы и выкиньте ArgumentNullException, если какой-либо из них является нулевым и предоставляет вам аргументы, которые были пустыми.

Кстати, в отношении повторяющихся вопросов о претензиях речь идет не о маркировке аргументов атрибутами или чем-то, что является встроенным, а в том, что некоторые называют его Guard Clauses, чтобы гарантировать, что объект получает инициализированные зависимости.

Ответ 1

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);
        }
    }
}

использование:

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

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

DebuggerStepThroughAttribute очень удобен, так что в случае исключения во время отладки (или когда я присоединяю отладчик после возникновения исключения) я не попаду внутрь метода ArgumentNotNull а вместо этого в вызывающем методе, где actually происходит нулевая ссылка.

Я использую аннотации контракта ReSharper.

  • ContractAnnotationAttribute гарантирует, что я никогда не опишу аргумент ("foo"), а также автоматически переименует его, если я переименую символ foo.
  • NotNullAttribute помогает ReSharper в анализе кода. Так что, если я сделаю new Constructor(null), то получит предупреждение от ReSharper, что это приведет к исключению.
  • Если вы не любите аннотировать свой код напрямую, вы можете сделать то же самое с внешними XML файлами, которые вы можете развернуть в своей библиотеке и которые пользователи могут ссылаться в своем ReShaprer.

Ответ 2

Если у вас слишком много параметров в ваших конструкторах, вам лучше их пересмотреть, но это еще одна история.

Чтобы уменьшить код проверки шаблона, многие ребята пишут классы утилиты Guard следующим образом:

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

    // other validation methods
}

(Вы можете добавить другие методы проверки, которые могут быть необходимы для этого класса Guard).

Таким образом, для проверки параметра требуется только одна строка кода:

    private static void Foo(object obj)
    {
        Guard.ThrowIfNull(obj, "obj");
    }

Ответ 3

Нулевые ссылки - это одна из проблем, с которыми вам приходится защищаться. Но они не единственные. Проблема шире, чем это, и это сводится к следующему: метод принимает экземпляры определенного типа, но не может обрабатывать все экземпляры.

Другими словами, область метода больше, чем набор значений, которые он обрабатывает. Оговорки о защите затем используются, чтобы утверждать, что фактический параметр не попадает в эту "серую зону" домена метода, которая не может быть обработана.

Теперь у нас есть нулевые ссылки, как значение, которое обычно выходит за пределы допустимого набора значений. С другой стороны, часто случается, что некоторые ненулевые элементы набора также неприемлемы (например, пустая строка).

В этом случае может оказаться, что подпись метода слишком широка, что указывает на проблему проектирования. Это может привести к редизайну, например. определяя подтип, как правило, производный интерфейс, который ограничивает область метода и устраняет некоторые из защитных предложений. Вы можете найти пример в этой статье: Почему нам нужны оговорки о безопасности?

Ответ 4

Существует специальный пакет под названием SwissKnife. Установите SwissKnife из галереи nuget. Он предоставляет вам множество опций, начиная с нулевой проверки аргументов Argument.IsNotNullOrEmpty(args,"args") в SwissKnife.Diagnostics.Contracts пространство имен вместе с опцией idoim и многие другие. Вы можете установить Option<Class_Name> _someVar и затем проверить, является ли _someVar.IsSome или _someVar.IsNone. Это также помогает против обнуляемых классов. Надеюсь это поможет.

Ответ 5

Вы можете попробовать мою библиотеку Heleonix.Guard, которая обеспечивает функции защиты.

Вы можете написать пункты охраны, как показано ниже:

// 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));

Он обеспечивает выдачу многих существующих исключений, и вы можете написать собственные методы расширения для пользовательских исключений. Кроме того, библиотека ссылается на библиотеку "Heleonix.Extensions" с предикативными расширениями, такими как IsNull, IsNullOrEmptyOrWhitespace, IsLessThan и многими другими, чтобы проверить ваши аргументы или переменные на соответствие требуемым значениям. В отличие от некоторых других защитных библиотек с гибкими интерфейсами, эти расширения не генерируют промежуточные объекты, и поскольку реализация действительно проста, они производительны.