当找不到值时,如果不是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的替代方法是什么呢?
对于单值情况,请考虑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
值传递。您会被明确警告:您可以得到一个客户,也可以没有客户。
为什么Maybe
比null
工作得更好?这是因为Maybe
是一个有良好数学基础的单子,而null
不是。List
也是一个monad,这就是它如此方便的原因。通过正确的方法,monad可以很好地组合在一起,这样您就不必到处检查HasValue
。对于null
,对于每个函数结果,您都会遇到C样式嵌套的if
。
例外情况也是monad!这就是为什么其他答案(正确地)建议抛出异常。除了例外,您甚至可以获得函数式编程的另一个典型优点,即模式匹配(在catch
子句中)。