Подтвердить что ты не робот

Throwing ArgumentNullException

Предположим, что у меня есть метод, который принимает какой-то объект в качестве аргумента. Теперь скажите, что если этому методу передан пустой аргумент, это фатальная ошибка и исключение должно быть выбрано. Стоит ли мне кодовое что-то вроде этого (имея в виду, что это тривиальный пример):

void someMethod(SomeClass x)
{
    if (x == null){
        throw new ArgumentNullException("someMethod received a null argument!");
    }

    x.doSomething();
}

Или мне безопасно просто полагаться на это, бросая NullException, когда он вызывает x.doSomething()?

Во-вторых, предположим, что someMethod является конструктором, и x не будет использоваться до вызова другого метода. Должен ли я немедленно исключить исключение или подождать, пока не понадобится x, и выкиньте исключение?

4b9b3361

Ответ 1

Я предпочитаю ArgumentNullException по NullReferenceException, который не проверяет аргумент. В общем, я предпочитаю всегда проверять недействительность, прежде чем пытаться вызвать метод на потенциально нулевом объекте.

Если метод является конструктором, то он будет зависеть от нескольких разных факторов: существует ли общедоступный параметр для свойства и насколько вероятно, что объект будет фактически использоваться. Если существует публичный сеттер, то не предоставление допустимого экземпляра через конструктор было бы разумным и не должно приводить к исключению.

Если нет публичного сеттера, и можно использовать содержащий объект без ссылки на инъецируемый объект, вы можете отложить проверку/исключение до тех пор, пока не будет предпринята попытка его использования. Я думаю, что общий случай, однако, заключался бы в том, что инъецируемый объект имеет важное значение для функционирования экземпляра, и поэтому исключение ArgumentNull является совершенно разумным, поскольку экземпляр не может функционировать без него.

Ответ 2

Я всегда следую практике не быстро. Если ваш метод зависит от X, и вы понимаете, что X может быть передан в null, null проверить его и сразу же вызвать исключение, вместо того чтобы продлить точку отказа.

Обновление 2016:

Пример реального мира. Я настоятельно рекомендую использовать Аннотации Jetbrains.

[Pure]
public static object Call([NotNull] Type declaringType, 
                          [NotNull] string methodName, 
                          [CanBeNull] object instance)
{
    if (declaringType == null) throw new ArgumentNullException(nameof(declaringType));
    if (methodName == null) throw new ArgumentNullException(nameof(methodName));

Отчеты Guard значительно улучшены с помощью С# 6, предоставляющего оператор nameof.

Ответ 3

Я предпочитаю явное исключение по этим причинам:

  • Если метод имеет более одного аргумента SomeClass, он дает вам возможность сказать, какой из них (все остальное доступно в стеке вызовов).
  • Что делать, если вы делаете что-то, что может иметь побочный эффект, прежде чем ссылаться на x?

Ответ 4

Я согласен с идеей быстрого провала - однако разумно знать, почему неудача быстро практична. Рассмотрим этот пример:

void someMethod(SomeClass x)
{       
    x.Property.doSomething();
}

Если вы полагаетесь на NullReferenceException, чтобы сказать вам, что что-то не так, как вы узнаете, что было пустым? Трассировка стека даст вам только номер строки, а не ссылку. В этом примере x или x.Property оба могут быть нулевыми и без сбоев быстро с агрессивной проверкой заранее, вы не будете знать, что это такое.

Ответ 5

Я бы предпочел проверку параметров с явным аргументом ArgumentNullException.

Глядя на метаданные:

 //
    // Summary:
    //     Initializes a new instance of the System.ArgumentNullException class with
    //     the name of the parameter that causes this exception.
    //
    // Parameters:
    //   paramName:
    //     The name of the parameter that caused the exception.
    public ArgumentNullException(string paramName);

Вы можете видеть, что строка должна быть именем параметра, равным null, и поэтому дать разработчику подсказку о том, что происходит не так.

Ответ 6

Лучше выкинуть ArgumentNullException раньше, чем позже. Если вы его выбросите, вы можете предоставить более полезную информацию о проблеме, чем исключение NullReferenceException.

Ответ 7

Вы должны явно передать ArgumentNullException, если вы ожидаете, что ввод не будет null. Возможно, вы захотите написать класс Guard, который предоставляет вспомогательные методы для этого. Таким образом, ваш код будет:

void someMethod(SomeClass x, SomeClass y)
{
    Guard.NotNull(x,"x","someMethod received a null x argument!");
    Guard.NotNull(y,"y","someMethod received a null y argument!");


    x.doSomething();
    y.doSomething();
}

Метод NonNull выполнит проверку недействительности и вызовет исключение NullArgumentException с сообщением об ошибке, указанным в вызове.

Ответ 8

1- Делайте это явно, если вам не нужно значение Null. В противном случае, когда кто-то еще посмотрит ваш код, они подумают, что принятие значения Null принимается.

2- Сделайте это быстрее, чем сможете. Таким образом, вы не распространяете "неправильное" поведение при наличии Null, если это не предполагается.

Ответ 9

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

Ответ 10

Я, вероятно, буду замедлен для этого, но я думаю, что совсем другое.

Как насчет следующей хорошей практики, называемой "никогда не пропускать null" и удалять уродливую проверку исключений?

Если параметр является объектом, НЕ ПЫТАЙТЕ NULL. Кроме того, НЕ ВОЗВРАТЬ НУЛЛ. Вы можете даже использовать шаблон объекта Null, чтобы помочь с этим.

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

Гораздо чище, чем уродливые исключения.

Ответ 11

Вы можете использовать синтаксис, как показано ниже, чтобы не просто бросать ArgumentNullException, но также иметь это имя исключения как часть текста ошибки. Например;.

void SomeMethod(SomeObject someObject)
{
    Throw.IfArgNull(() => someObject);
    //... do more stuff
}

Класс, используемый для повышения исключения:

public static class Throw
{
    public static void IfArgNull<T>(Expression<Func<T>> arg)
    {
        if (arg == null)
        {
            throw new ArgumentNullException(nameof(arg), "There is no expression with which to test the object value.");
        }

        // get the variable name of the argument
        MemberExpression metaData = arg.Body as MemberExpression;
        if (metaData == null)
        {
            throw new ArgumentException("Unable to retrieve the name of the object being tested.", nameof(arg));
        }

        // can the data type be null at all
        string argName = metaData.Member.Name;
        Type type = typeof(T);
        if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
        {
            throw new ArgumentException("The expression does not specify a nullible type.", argName);
        }

        // get the value and check for null
        if (arg.Compile()() == null)
        {
            throw new ArgumentNullException(argName);
        }
    }
}