当不需要检查空值时,有哪些指导方针?
我最近一直在处理的许多继承代码都带有令人作呕的空检查。对琐碎函数进行空检查,对声明非空返回的 API 调用进行空检查等。在某些情况下,空检查是合理的,但在许多地方,空值并不是一个合理的期望。
我听过很多论点,从“你不能相信其他代码”到“总是防御性地编程”再到“在语言保证给我一个非空值之前,我总是会检查”。在某种程度上,我当然同意其中的许多原则,但我发现过多的空值检查会导致其他通常违反这些原则的问题。顽强的空值检查真的值得吗?
我经常观察到带有过多空值检查的代码实际上质量较差,而不是质量较高。许多代码似乎过于关注空值检查,以至于开发人员忽略了其他重要品质,例如可读性、正确性或异常处理。特别是,我看到很多代码忽略了 std::bad_alloc 异常,但对new
.
在 C++ 中,由于取消引用空指针的不可预测的行为,我在某种程度上理解了这一点;在 Java、C#、Python 等中,null 取消引用的处理更加优雅。我是否刚刚看到了警惕的 null 检查的不良示例,或者这真的有什么问题吗?
这个问题旨在与语言无关,尽管我主要对 C++、Java 和 C# 感兴趣。
我见过的一些似乎过度的空值检查示例包括以下内容:
这个示例似乎考虑了非标准编译器,因为 C++ 规范说失败的新编译器会引发异常。除非您明确支持不兼容的编译器,否则这有意义吗?这在 Java 或 C#(甚至 C++/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's 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;
}