Допустим, у меня есть метод:
public void DoSomething(ISomeInterface someObject)
{
if(someObject == null) throw new ArgumentNullException("someObject");
someObject.DoThisOrThat();
}
Меня научили считать, что выбрасывание ArgumentNullException
является «правильным», но ошибка «Ссылка на объект не установлена на экземпляр объекта» означает, что у меня есть ошибка.
Зачем?
Я знаю, что если я кеширую ссылку someObject
и использую ее позже, то лучше проверять ее на недействительность, если она передана, и потерпеть неудачу рано. Однако, если я разыменую его в следующей строке, почему мы должны делать проверку? Это будет выбрасывать исключение, так или иначе.
Редактировать :
Это просто пришло мне в голову ... исходит ли страх перед разыменованным нулем от языка, подобного C ++, который не проверяет вас (то есть он просто пытается выполнить какой-то метод в ячейке памяти + смещение метода)?
Ответы:
Я полагаю, что если вы немедленно разыменуете переменную, вы можете обсудить в любом случае, но я все равно предпочел бы исключение ArgumentNullException.
Это намного более ясно о том, что происходит. Исключение содержит имя переменной с нулевым значением, а исключение NullReferenceException - нет. В частности, на уровне API ArgumentNullException проясняет, что некоторые вызывающие не выполняли контракт метода, и ошибка не обязательно была случайной ошибкой или какой-то более глубокой проблемой (хотя это все еще может иметь место). Вы должны потерпеть неудачу рано.
Кроме того, что более поздние сопровождающие будут делать чаще? Добавить ArgumentNullException, если они добавляют код до первой разыменования, или просто добавить код, а затем разрешить создание исключения NullReferenceException?
источник
Предположим, я вызываю метод,
M(x)
который вы написали. Я передаю ноль. Я получаю ArgumentNullException с именем, установленным на «х». Это исключение однозначно означает, что у меня есть ошибка; Я не должен был передать ноль для х.Предположим, я вызываю метод M, который вы написали. Я передаю ноль. Я получаю исключение nure deref. Как, черт возьми, я должен знать, есть ли у меня ошибка, или у вас есть ошибка, или в какой-то сторонней библиотеке, которую вы вызывали от моего имени, есть ошибка? Я ничего не знаю о том, нужно ли мне исправить свой код или позвонить вам, чтобы сказать, как исправить ваш код.
Оба исключения указывают на ошибки; ни один никогда не должен быть брошен в производственный код. Вопрос в том, какое исключение сообщает, у кого ошибка лучше человеку, выполняющему отладку? Исключение Null deref почти ничего вам не говорит; Аргумент null исключение говорит вам многое. Будьте добры к своим пользователям; бросать исключения, которые позволяют им учиться на своих ошибках.
источник
"neither should ever be thrown in production code"
какие другие типы кода / ситуаций они будут использовать? Должны ли они быть составлены какDebug.Assert
? Или ты имеешь в виду не выбрасывать их из API?throw new ArgumentNullException
в своем рабочем коде, и он никогда не должен запускаться вне тестового случая . Исключение на самом деле никогда не должно вызываться, потому что вызывающая сторона никогда не должна иметь эту ошибку.Дело в том, что ваш код должен делать что-то значимое.
А
NullReferenceException
не имеет особого значения, потому что это может значить обо всем. Не смотря на то, где было сгенерировано исключение (и код может быть недоступен для меня), я не могу понять, что пошло не так.Значение
ArgumentNullException
имеет значение, потому что оно говорит мне: операция завершилась неудачно, потому что аргументsomeObject
былnull
. Мне не нужно смотреть на ваш код, чтобы понять это.Кроме того, поднятие этого исключения означает, что вы обрабатываете явным образом
null
, даже если ваш единственный способ сделать это - сказать, что вы не можете его обработать, хотя простое падение кода потенциально может означать, что вы на самом деле сможете обрабатывать нуль , но забыл реализовать дело.Исключением является передача информации в случае сбоя, которая может быть обработана во время выполнения для восстановления после сбоя или во время отладки, чтобы лучше понять, что пошло не так.
источник
Я полагаю, что единственным преимуществом является то, что вы можете обеспечить лучшую диагностику за исключением. То есть вы знаете, что вызывающая функция передает значение null, в то время как если вы разрешите разыменованию бросить ее, вы не будете уверены в том, передает ли вызывающая функция неверные данные или имеется ошибка в самой функции.
Я сделал бы это, если бы кто-то другой вызвал функцию, чтобы упростить ее использование (отладку, если что-то пойдет не так), а не делал это в моем внутреннем коде, потому что среда выполнения все равно проверит это.
источник
У вас есть ошибка, если код не работает как задумано.
NullReferenceException
Выбрасывается системой , и этот факт действительно делает больше , чем ошибка в функции. Не только потому, что вы не определили конкретное исключение для обработки. Кроме того, поскольку разработчик, читающий ваш код, может предположить, что вы не учли возникшую ситуацию.По тем же причинам, что
ApplicationException
принадлежит вашему приложению против общего,Exception
который принадлежит операционной системе. Тип создаваемого исключения указывает, откуда возникло исключение.Если вы учли значение null, лучше, чтобы вы обрабатывали его аккуратно, выбрасывая конкретное исключение или, если это приемлемый ввод, то не выбрасывайте исключение, потому что они дороги. Обрабатывайте его, выполняя операции с хорошими значениями по умолчанию или вообще без операций (например, обновление не требуется).
Наконец, не пренебрегайте способностью звонящего справиться с ситуацией. Предоставляя им более конкретное исключение,
ArgumentException
они могут построить свой try / catch таким образом, чтобы обработать эту ошибку до более общего,NullReferenceException
что может быть вызвано любым количеством других причин, которые встречаются в другом месте, например, неудачей инициализации переменной конструктора.источник
Проверка делает более ясным, что происходит, но настоящая проблема заключается в коде, подобном этому (надуманному) примеру:
Здесь задействована цепочка вызовов, и без нулевой проверки вы видите ошибку только в someOtherObject, а не там, где проблема впервые обнаружила себя, что мгновенно означает потерю времени на отладку или интерпретацию стеков вызовов.
В общем, лучше быть согласованным с такими вещами и всегда добавлять нулевую проверку - что позволяет легче определить, отсутствует ли одна из них, а не выбирать и выбирать в каждом конкретном случае (при условии, что вы знаете, что нулевое значение всегда недействительным).
источник
Другой причиной для рассмотрения является то, что если некоторая обработка происходит перед использованием нулевого объекта?
Скажем, у вас есть файл данных связанных идентификаторов компании (Cid) и идентификаторов людей (Pid). Он сохраняется в виде потока числовых пар:
Сид Сид Сид Сид Сид Сид Сид Сид
И эта функция VB записывает записи в файл:
Если
Person
это нулевая ссылка, строкаFile.Write(Person.ID)
выдаст исключение. Программное обеспечение может поймать исключение и восстановить, но это оставляет проблему. Идентификатор компании был написан без идентификатора связанного лица. На самом деле, когда вы в следующий раз вызовете эту функцию, вы поставите идентификатор компании там, где ожидался предыдущий идентификатор. Кроме того, что эта запись неверна, каждая последующая запись будет иметь идентификатор человека, за которым следует идентификатор компании, что является неправильным способом.Сид Сид Сид Сид Сид Сид Сид Сид Сид
Проверяя параметры в начале функции, вы предотвращаете любую обработку, когда метод гарантированно завершится с ошибкой.
источник