当找不到值时,如果不是null,应该返回什么

本文关键字:返回 什么 null 如果不 找不到 | 更新日期: 2023-09-27 18:26:38

无论我读到哪里,都不断重复"从不返回null"的想法,但在找不到值的情况下,如果不为null,我应该返回什么?

采用以下方法

List<Customer> GetCustomerListByRoleID(Guid RoleID) {}

在这种情况下(在大多数复数方法中),如果我们找不到我们的值并且我们很好,那么很容易简单地返回一个空的List<Customer>

然而,在这样的方法的情况下

Customer GetCustomerByID(Guid CustomerID) {}

您没有返回空数组的奢侈。实际上,你所能做的就是返回一个New Customer();,但随后你有一个潜在的未初始化对象(你仍然需要检查)或null。

那么,在奇异方法中返回null的替代方法是什么呢?

当找不到值时,如果不是null,应该返回什么

对于单值情况,请考虑TryGet模式

bool TryGetCustomerByID(Guid CustomerID, out Customer customer) { }

这清楚地表达了这种方法可能会失败的意图,并使忽视失败的方面变得更加尴尬。正确处理它会产生可读性很强的代码。

Customer c;
if (container.TryGetCustomer(id, out c)) {
  ...
} else {
  // Deal with failure
}

我经常喜欢将我的TryGet API与一个版本配对,该版本在调用方知道必须存在的情况下抛出,否则就违反了某种隐含的约定。

Customer GetCustomerByID(Guid id) {
  Customer c;
  if (!TryGetCustomerByID(id, out c)) {
    throw new Exception(...);
  }
  return c;
}

我认为这取决于询问一个不存在的客户是否应该被视为一个错误。两个明显的选择是:

  • 返回null
  • 抛出异常(CustomerNotFoundException或类似的东西)

在您获得Guid的情况下,感觉有一个明确的期望,即代表客户。尝试在存在Guid机会上获取该客户是不正常的。

可以返回Tuple<Customer, bool>TryGetCustomer,但老实说,我认为在这种情况下,这两者都过于复杂。

最重要的是记录将要发生的事情。。。特别是,如果您的代码永远不会返回null,请在文档中声明。(或者考虑使用代码契约来提供相同的信息…)

您可以定义一个表示null客户的静态Customer对象(即Customer.Name="NOT_FOUND"),并返回该对象而不是null。然后在调用方法中,只需将返回的值与该静态客户进行比较。

edit:尽管我承认我宁愿只返回null。

为什么不鼓励重新调用null?其思想是,如果函数的返回类型是T,那么您总是得到一个类型为T的对象,而null肯定是,而不是T类型的对象,甚至不是T类型的零对象。

getCustomers()的情况下,您会得到一个List<Customer>,它是0到n个Customer对象。

getCustomer()的情况下,您想要0或1个Customer对象。在Haskell中,您可以得到ADT和Maybe,在Scala中,您有case类和Option。你能在C#中使用什么?嗯,也许在C#中有实现,它内部非常简单。

使用Maybe<Customer> getCustomer(),可以保证不会将其结果作为空值而不是Customer值传递。您会被明确警告:您可以得到一个客户,也可以没有客户。

为什么Maybenull工作得更好?这是因为Maybe是一个有良好数学基础的单子,而null不是。List也是一个monad,这就是它如此方便的原因。通过正确的方法,monad可以很好地组合在一起,这样您就不必到处检查HasValue。对于null,对于每个函数结果,您都会遇到C样式嵌套的if

例外情况也是monad!这就是为什么其他答案(正确地)建议抛出异常。除了例外,您甚至可以获得函数式编程的另一个典型优点,即模式匹配(在catch子句中)。