Должен ли я проверить, является ли аргумент null, если я собираюсь использовать его немедленно?

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

if(arg == null)
    throw new ArgumentNullException(nameof(arg));

но что, если я собираюсь использовать arg немедленно? Я должен проверить? Я имею в виду, если я этого не сделаю, окружающая среда бросит для меня (исключение NullReferenceException) в любом случае.

например:

public int DoStuff(object o)
{
    return o.GetHashCode();
}

Я мог бы написать добавить значение null проверить легко:

public int DoStuff(object o)
{
    if(o == null)
        throw new ArgumentNullException(nameof(o));
    return o.GetHashCode();
}

но в обоих случаях будет создано исключение (почти в одной и той же строке для целей отладки). Единственная разница-тип.

вопрос: On общественные методы с аргумент одного ссылочного типа, если я собираюсь использовать аргумент тут, должен ли я еще Регистрация это null?

7 ответов


предлагаю проверка, потому что у вас есть два исключения типа:

  1. неожиданный NullReferenceException - что-то пошло не так, и вы должны отладить самостоятельно
  2. ArgumentNullException - the аргумент равно null, и это вызывающий, который ошибается (а не код реагирующая право)

бросать ArgumentNullException вроде контракт: Я сделаю вещь в случае, если аргумент верен:

  // An exaggerated example 
  public int DoStuff(SomeObject o) {
    if (o == null)
      throw new ArgumentNullException(nameof(o));
    else if (o.Disposed)
      throw new ArgumentException(nameof(o), "o must not be disposed")  
    else if (o.Id > 100)
      throw new ArgumentOutOfRangeException("Id ...", nameof(o));  

    // o meets the contract, we accept it and thus we guarantee the output

    return o.SomeMethod();
  }

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


Ну, это зависит от ;) Это мой взгляд на это, и вопрос (своего рода) субъективен.

IMO, если код выдает исключение NullReferenceException, это означает, что сам код не имеет внутренней согласованности. Это признак распущенности, и его следует избегать любой ценой. Если метод создает исключение NullReferenceException, это ошибка метода.

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

итог: исключение ArgumentNullException-это контракт, исключение NullReferenceException-это ошибка.


Я полагаю, если вы сразу разыменовываете переменную, вы можете спорить в любом случае, но я все равно предпочел бы ArgumentNullException. Это гораздо более ясно о том, что происходит. Исключение содержит имя переменной, которая была null, в то время как исключение NullReferenceException нет.


я настоятельно рекомендую вам проверить null в верхней части метода.

подумайте об этом как о "контракте", который указывает следующее:

для выполнения этого метода этот аргумент должен быть ненулевым.

выполнение проверки null-отличная документация. Вы можете сделать еще один шаг, используя комментарии XML, например

/// <summary>
/// 
/// </summary>
/// <param name="arg1"></param>
/// <exception cref="ArgumentNullException">If <paramref name="arg1"/> is NULL.</exception>
private static void Foo(object arg1)
{
}

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

public static int DoStuff(string o) {
    return Int.Parse(o);
}

Если у вас есть это, то это действительно не имеет значения.

public static void main(string[] args)
{
    try {
        Console.Write("Enter a number: ");
        value = DoStuff(Console.ReadLine());
    }
    catch(Exception ex) {
        Console.WriteLine("An exception was thrown.  Contents: " + ex.ToString());
    }
}

В любом случае поток вашей программы одинаков. Но если у вас есть ниже:

public static void main(string[] args)
{
    try {
        int value = 0;
        do {
            try {
                Console.Write("Enter a non-zero number to stop: ");
                value = DoStuff(Console.ReadLine());
            }
            catch(ArgumentException ex) {
                Console.WriteLine("Problem with input, try again.  Exception message was: " + ex.Message);
                continue; // Or just nothing, will still loop
            }
        } while(value == 0);
    }
    catch(Exception ex) {
        Console.WriteLine("An exception was thrown.  Contents: " + ex.ToString());
    }
}

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

но я согласен с постом Дмитрия выше, что публичный метод имеет гораздо большее значение, чем частный. Ваша подпись для метода чем-то напоминает контракт. Лучше всего обеспечить его соблюдение.


есть несколько различных сценариев, я думаю, что вы должны рассмотреть:

  1. метод вызывается из вашей собственной программы. Вы не ожидаете значения null. Вы используете объект где-то в конце строки, и если он равен null, он все равно изменит состояние.
  2. метод вызывается из вашей собственной программы. Вы не ожидаете значения null. Если это null, это не изменит состояние, и все будет продолжать быть последовательным.
  3. метод доступен через библиотеку или API. Неизвестный разработчик X вызывает метод с тем, что вы хотите.
  4. вы просто хотите знать, что аргумент не null при реализации вещи. Чтобы защитить себя и членов своей команды.

точки (1) и (2) о исключение безопасности. В основном это означает, что независимо от вашего ввода состояние ваших структур данных должно быть согласованным. В большинстве случаев это решается с помощью простой проверки; в более сложный сценарий может включать в себя откат или более сложный механизм. Итак:

  1. если добавить null значение для базы данных, вы не хотите, чтобы он испортил вашу базу данных. Теперь вы можете, конечно, решить null значения, просто проверяя ваше предположение. Если аргумент null ставим ArgumentNullException потому что это лучше всего описывает сценарий.
  2. если вы вызываете метод, который не ожидает null значение и оно не повлияет на остальная часть вашей системы, я обычно оставляю комментарий где-то и позволяю природе идти своим путем. А NullReferenceException говорит мне достаточно о природе ошибки.
  3. если это API, люди ожидают определенного поведения (например, a ArgumentNullException). Кроме того, как разработчик вы не должны доверять большой плохой внешний мир, так что вы должны проверить в любом случае.
  4. если вы просто хотите защитить себя и членов вашей команды, использовать Assertion. Утверждения обычно реализуются таким образом, что они не заканчиваются в код выпуска (или, по крайней мере, не влияет на вашу скорость) автоматически вызовет точку останова и провалит модульный тест. Их также легко распознать, поэтому они не будут добавлять слишком много раздувания в ваш код.

подводя итог, я бы не просто поставил чеки везде; они, как правило, делают ваш код нечитаемым и раздутым. Тем не менее, если бы я поставил чек, чтобы убедиться исключение безопасности лучше всего использовать исключение, которое лучше всего описывает ошибку.


следуйте Эйнштейну- "сделайте вещи как можно проще, но не проще". Этот код делает то же самое без строки кода? Если это так, то, вероятно, лучше удалить его.