Каковы некоторые рекомендации, когда не необходимо проверить нуль?
Множество унаследованных кодов, над которыми я работал, с опозданием имеет ноль-проверку ad nauseam. Null проверяет тривиальные функции, проверяет null на вызовах API, которые указывают непустые возвраты и т.д. В некоторых случаях нулевые проверки являются разумными, но во многих местах нуль не является разумным ожиданием.
Я слышал несколько аргументов, начиная от "Нельзя доверять другому коду", чтобы "ВСЕГДА программа защищалась" до "Пока язык не гарантирует мне ненулевое значение, я всегда буду проверять". Я определенно согласен со многими из этих принципов до определенного момента, но я обнаружил, что чрезмерная проверка нулевого значения вызывает другие проблемы, которые обычно нарушают эти принципы. Является ли цепкая нулевая проверка действительно стоящей?
Часто я наблюдал коды с избыточной нулевой проверкой, чтобы быть на самом деле более низкого качества, а не более высокого качества. Большая часть кода, похоже, настолько сфокусирована на нулевых проверках, что разработчик потерял из виду другие важные качества, такие как читаемость, правильность или обработка исключений. В частности, я вижу, что много кода игнорирует исключение std:: bad_alloc, но выполняет нулевую проверку на new
.
В С++ я это понимаю в некоторой степени из-за непредсказуемого поведения разыменования нулевого указателя; Нулевое разынение обрабатывается более изящно в Java, С#, Python и т.д. Я только что видел плохие примеры бдительной проверки нулевого кода или есть что-то в этом роде?
Этот вопрос предназначен для языкового агностика, хотя меня в основном интересуют С++, Java и С#.
Некоторые примеры нулевой проверки, которые я видел, которые кажутся чрезмерными, включают следующее:
Этот пример, по-видимому, относится к нестандартным компиляторам, поскольку спецификация С++ говорит, что неудавшийся новый генерирует исключение. Если вы явно не поддерживаете несогласованные компиляторы, имеет ли это смысл? Имеет ли это смысл в управляемом языке, таком как Java или С# (или даже С++/CLR)?
try {
MyObject* obj = new MyObject();
if(obj!=NULL) {
//do something
} else {
//??? most code I see has log-it and move on
//or it repeats what in the exception handler
}
} catch(std::bad_alloc) {
//Do something? normally--this code is wrong as it allocates
//more memory and will likely fail, such as writing to a log file.
}
Другим примером является работа с внутренним кодом. В частности, если это небольшая команда, которая может определить свои собственные методы развития, это кажется ненужным. В некоторых проектах или устаревшем коде доверенная документация может быть нецелесообразна... но для нового кода, который вы или ваша команда контролируете, действительно ли это необходимо?
Если метод, который вы можете видеть и может обновлять (или кричать на разработчика, который несет ответственность), имеет контракт, нужно ли еще проверять нули?
//X is non-negative.
//Returns an object or throws exception.
MyObject* create(int x) {
if(x<0) throw;
return new MyObject();
}
try {
MyObject* x = create(unknownVar);
if(x!=null) {
//is this null check really necessary?
}
} catch {
//do something
}
При разработке частной или другой внутренней функции действительно ли необходимо явно обрабатывать нуль, когда контракт требует только ненулевые значения? Почему нулевой чек предпочтительнее утверждения?
(очевидно, что в вашем общедоступном API нулевые проверки имеют жизненно важное значение, так как считалось невежливым кричать на ваших пользователей за неправильное использование API)
//Internal use only--non-public, not part of public API
//input must be non-null.
//returns non-negative value, or -1 if failed
int ParseType(String input) {
if(input==null) return -1;
//do something magic
return value;
}
По сравнению с:
//Internal use only--non-public, not part of public API
//input must be non-null.
//returns non-negative value
int ParseType(String input) {
assert(input!=null : "Input must be non-null.");
//do something magic
return value;
}