我读到的每个地方都不断重写“永远不会返回空值”的想法,但如果在找不到值的情况下,如果不为null,我应该返回什么?
采取以下方法
List<Customer> GetCustomerListByRoleID(Guid RoleID) {}
在这种情况下(在大多数复数方法中)如果我们找不到我们的价值并且我们很好,那么很容易简单地返回一个空的List<Customer>
。
但是在类似
的方法的情况下Customer GetCustomerByID(Guid CustomerID) {}
你没有返回空数组的奢侈。事实上你所能做的就是返回一个New Customer();
,但是你有一个潜在的未初始化对象(你仍需要检查)或null。
那么在单数方法中返回null的推荐替代方法是什么?
答案 0 :(得分:10)
对于单值情况,请考虑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;
}
答案 1 :(得分:0)
我认为这取决于是否应该将不存在的客户视为错误。两个明显的选择是:
null
CustomerNotFoundException
或类似的东西)在您获得Guid
的情况下,感觉就像将代表客户的明确期望一样。尝试使用 offchance 上的Guid
来获取客户是不正常的。
你可以返回Tuple<Customer, bool>
或TryGetCustomer
- 但说实话,我认为这两种情况都过于复杂。
大多数重要的是你记录会发生什么......特别是,如果你的代码永远不会返回null,请在文档中说明。 (或者考虑使用代码合同来提供相同的信息......)
答案 2 :(得分:0)
您可以定义一个静态Customer对象,该对象表示一个空客户(即Customer.Name =“NOT_FOUND”,并返回该对象而不是null。然后在调用方法中,只需将返回值与该静态客户进行比较。 / p>
编辑:虽然我承认只想返回null。
答案 3 :(得分:0)
为什么不鼓励重新调整null
?我们的想法是,如果函数的返回类型为T
,则总是会得到一个T
类型的对象,而null
肯定是而不是{{1}类型的对象},甚至不是T
类型的零对象。
如果是T
,则会得到一个getCustomers()
,它是0到List<Customer>
个对象。
如果是Customer
,您需要0或1个getCustomer()
个对象。在Haskell中,您获得了ADT和Customer
,在Scala中,您有案例类和Maybe
。你能在C#中使用什么?嗯,有Maybe implementaion in C#,内部非常简单。
使用Option
,您可以保证不将其结果作为空值而不是Maybe<Customer> getCustomer()
值传递。您被明确警告:您可以获得客户,也可以不获得客户。
为什么Customer
的效果优于Maybe
?这是因为null
是一个具有良好数学基础的monad,而Maybe
则不是。null
。 List
也是一个monad,这就是它如此方便的原因。使用正确的方法monads can be combined in nice ways,这样您就无需在任何地方检查HasValue
。对于null
,您会遇到每个函数结果的C风格嵌套if
。
例外也是一个单子!这就是为什么其他答案(正确)建议抛出异常的原因。除了例外,你甚至可以获得另一个典型的函数式编程,即模式匹配(在catch
子句中)。