c# - 多少空检查就足够了?

标签 c# java c++ design-by-contract

对于何时不需要检查空值有哪些指导原则?

我最近一直在处理的许多继承代码都有空检查令人作呕。对普通函数进行空值检查,对声明非空返回的 API 调用进行空值检查等。在某些情况下,空值检查是合理的,但在许多地方,空值并不是一个合理的预期。

我听到过很多论点,从“你不能信任其他代码”到“总是防御性地编程”再到“在语言保证给我一个非空值之前,我总是会检查”。在某种程度上,我当然同意其中的许多原则,但我发现过多的空值检查会导致其他通常违反这些原则的问题。顽强的空值检查真的值得吗?

我经常观察到带有过多空值检查的代码实际上质量较差,而不是质量较高。许多代码似乎过于关注空值检查,以至于开发人员忽略了其他重要品质,例如可读性、正确性或异常处理。特别是,我看到很多代码忽略了 std::bad_alloc 异常,但对 new 进行了空检查。

在 C++ 中,由于取消引用空指针的不可预测的行为,我在某种程度上理解这一点;在 Java、C#、Python 等中可以更优雅地处理 null 取消引用。我是否刚刚看到过警惕的 null 检查的糟糕示例,或者这真的有什么问题?

这个问题旨在与语言无关,尽管我主要对 C++、Java 和 C# 感兴趣。


我见过的一些似乎过度的空值检查示例包括:


这个例子似乎考虑了非标准编译器,因为 C++ 规范说失败的 new 会引发异常。除非您明确支持不兼容的编译器,否则这有意义吗?这在 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.
}

另一个例子是在处理内部代码时。特别是,如果是一个可以定义自己的开发实践的小团队,这似乎是不必要的。在某些项目或遗留代码中,信任文档可能不合理……但对于您或您的团队控制的新代码,这真的有必要吗?

如果你可以看到并且可以更新(或者可以对负责的开发人员大喊大叫)的方法有契约(Contract),是否仍然需要检查空值?

//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
}

在开发私有(private)或其他内部函数时,当合约仅调用非空值时,是否真的需要显式处理空值?为什么空检查比断言更可取?

(显然,在您的公共(public) 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;
}

最佳答案

需要记住的一点是,您今天编写的代码虽然可能是一个小团队并且您可以拥有良好的文档,但会变成其他人必须维护的遗留代码。我使用以下规则:

  1. 如果我正在编写一个将公开给其他人的公共(public) API,那么我将对所有引用参数进行空值检查。

  2. 如果我正在为我的应用程序编写一个内部组件,当我需要在 null 存在时做一些特殊的事情时,或者当我想让它非常清楚时,我会编写 null 检查。否则我不介意得到空引用异常,因为这也很清楚发生了什么。

  3. 在处理来自其他人框架的返回数据时,我只在可能返回 null 并且有效时检查 null。如果他们的契约(Contract)说它不返回空值,我不会进行检查。

关于c# - 多少空检查就足够了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/302736/

相关文章:

c++ - 多级预处理器宏搞乱了字符串化输出

c# - 使用 linq 查找索引位置,但要考虑依赖于值的关系

c# - .NET Core 6,选项模式,如何从 appsettings 获取 json 对象数组

c++ - C++11 中的优雅时间打印

c++ - 在 C++ 中使用 string::erase 提取数据

java - 我正在尝试实现一个资源处理程序类。它必须是无死锁和无饥饿的

c# - 这个图案的名字? (回答 : lazy initialization with double-checked locking)

Linux 上的 C# - 有人根据使用单声道的经验得到意见吗?

Java FX TranslateTransition 持续时间更改

java - NetBeans 中的 "No primary keys"错误