Существуют ли в C # какие-либо веские причины (кроме более точного сообщения об ошибке) для добавления проверки нулевого параметра к каждой функции, где значение null не является допустимым значением? Очевидно, что код, использующий s, в любом случае вызовет исключение. И такие проверки делают код медленнее и труднее поддерживать.
void f(SomeType s)
{
if (s == null)
{
throw new ArgumentNullException("s cannot be null.");
}
// Use s
}
Ответы:
Да, на то есть веские причины:
NullReferenceException
Теперь что касается ваших возражений:
И для вашего утверждения:
В самом деле? Рассматривать:
void f(SomeType s) { // Use s Console.WriteLine("I've got a message of {0}", s); }
Это используется
s
, но не вызывает исключения. Еслиs
значение null недопустимо , и это указывает на то, что что-то не так, исключение является наиболее подходящим поведением здесь.Другое дело, куда вы помещаете эти проверки валидации аргументов. Вы можете решить доверять всему коду в своем классе, поэтому не беспокойтесь о частных методах. Вы можете доверять остальной части вашей сборки, поэтому не беспокойтесь о внутренних методах. Вы почти наверняка должны проверить аргументы для общедоступных методов.
Боковое примечание: перегрузка конструктора с одним параметром
ArgumentNullException
должна быть просто именем параметра, поэтому ваш тест должен быть:if (s == null) { throw new ArgumentNullException("s"); }
В качестве альтернативы вы можете создать метод расширения, позволяющий более кратко:
s.ThrowIfNull("s");
В моей версии (общего) метода расширения я заставляю его возвращать исходное значение, если оно не равно нулю, что позволяет писать такие вещи, как:
this.name = name.ThrowIfNull("name");
У вас также может быть перегрузка, которая не принимает имя параметра, если вас это не слишком беспокоит.
источник
ThrowIfEmpty
наICollection
Debug.Assert
. Еще важнее выявлять ошибки в продакшене (до того, как они испортят реальные данные), чем в разработке.throw new ArgumentNullException(nameof(s))
Я согласен с Джоном, но я бы добавил к этому одну вещь.
Мое отношение к тому, когда следует добавлять явные нулевые проверки, основано на следующих предпосылках:
throw
заявления есть заявления .if
является заявление .throw
вif (x == null) throw whatever;
Если нет возможности выполнить этот оператор, его нельзя проверить, и его следует заменить на
Debug.Assert(x != null);
.Если есть возможный способ выполнения этого оператора, напишите его, а затем напишите модульный тест, который его проверяет.
Это особенно важно , чтобы государственные методы общественных типов проверить свои аргументы таким образом; вы не представляете, какие безумные поступки собираются делать ваши пользователи. Скажите им: "Эй, тупица, ты делаешь неправильно!" исключение как можно скорее.
Частные методы частных типов, напротив, гораздо более вероятны в ситуации, когда вы управляете аргументами и можете иметь строгую гарантию того, что аргумент никогда не будет нулевым; используйте утверждение, чтобы задокументировать этот инвариант.
источник
Пользуюсь вот этим уже год:
_ = s ?? throw new ArgumentNullException(nameof(s));
Это единичная строка, и discard (
_
) означает, что ненужного выделения памяти нет.источник
Без явной
if
проверки может быть очень сложно выяснить, что было,null
если вы не владеете кодом.Если вы извлечете
NullReferenceException
из глубины библиотеки без исходного кода, у вас, вероятно, будет много проблем с выяснением того, что вы сделали не так.Эти
if
проверки не сделают ваш код заметно медленнее.Обратите внимание, что параметр
ArgumentNullException
конструктора - это имя параметра, а не сообщение.Ваш код должен быть
if (s == null) throw new ArgumentNullException("s");
Я написал фрагмент кода, чтобы упростить задачу:
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Check for null arguments</Title> <Shortcut>tna</Shortcut> <Description>Code snippet for throw new ArgumentNullException</Description> <Author>SLaks</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> <SnippetType>SurroundsWith</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>Parameter</ID> <ToolTip>Paremeter to check for null</ToolTip> <Default>value</Default> </Literal> </Declarations> <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$"); $end$]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>
источник
Возможно, вы захотите взглянуть на Кодовые контракты, если вам нужен более удобный способ убедиться, что вы не получаете никаких нулевых объектов в качестве параметра.
источник
Основное преимущество заключается в том, что вы с самого начала четко указываете требования к своему методу. Это дает понять другим разработчикам, работающим над кодом, что отправка вызывающего объекта значения null в ваш метод действительно является ошибкой.
Проверка также остановит выполнение метода до выполнения любого другого кода. Это означает, что вам не придется беспокоиться об изменениях, внесенных в метод, которые остались незавершенными.
источник
Это избавляет от некоторой отладки, когда вы попадаете в это исключение.
ArgumentNullException явно заявляет, что это было «s», которое было пустым.
Если у вас нет этой проверки и вы позволите коду взорваться, вы получите исключение NullReferenceException из какой-то неопознанной строки в этом методе. В сборке выпуска вы не получаете номеров строк!
источник
Исходный код:
void f(SomeType s) { if (s == null) { throw new ArgumentNullException("s cannot be null."); } // Use s }
Перепишите его как:
void f(SomeType s) { if (s == null) throw new ArgumentNullException(nameof(s)); }
Причина для переписывания using
nameof
заключается в том, что это позволяет упростить рефакторинг. Если имя вашей переменнойs
когда-либо изменится, то сообщения отладки также будут обновлены, тогда как если вы просто жестко закодируете имя переменной, тогда оно в конечном итоге станет устаревшим, когда обновления будут производиться с течением времени. Это хорошая практика, используемая в отрасли.источник
int i = Age ?? 0;
Итак, для вашего примера:
if (age == null || age == 0)
Или же:
if (age.GetValueOrDefault(0) == 0)
Или же:
if ((age ?? 0) == 0)
Или тройной:
int i = age.HasValue ? age.Value : 0;
источник