我们目前正在经历为C#编写一些编码标准的漫长过程。
我最近用签名
写了一个方法string GetUserSessionID(int UserID)
如果没有为用户找到会话,GetUserSession()将返回null。
在我的通话代码中......我说......
string sessionID = GetUserSessionID(1)
if (null == sessionID && userIsAllowedToGetSession)
{
session = GetNewUserSession(1);
}
在最近的一次代码审查中,审阅者说“你永远不应该从方法中返回null,因为它会在调用方法上调查空值。”
我立刻哭了恶作剧,好像你回来了。很多时候你还要对返回的值进行某种检查。
if (string.Empty == sessionID)
然而,考虑到这一点,我不会在Collection / Array / List的情况下返回null。我会返回一个空列表。
对此的解决方案(我认为)将重构为2种方法。
bool SessionExists(int userID);
和
string GetUserSessionID(int UserID);
这一次,GetUserSessionID会抛出一个SessionNotFound异常(因为它不应该返回null)
现在代码看起来像......
if(!SessionExists(1) && userIsAllowedToGetSession))
{
session = GetNewUserSession(1);
}
else
{
session = GetUserSessionID(1);
}
现在这意味着没有空值,但对我来说这似乎有点复杂。这也是一个非常简单的例子,我想知道这将如何影响更复杂的方法。
关于何时抛出异常以及如何处理异常,有很多关于最佳做法的建议,但似乎关于使用null的信息较少。
是否有其他人对空值的使用有任何可靠的指导(甚至更好的标准),这对于可空类型意味着什么(我们应该使用它们吗?)
提前致谢,
克里斯。
=====
我已经给了egaga的答案,因为我喜欢将Get vs Find作为编码指南的建议,但所有这些都是有趣的答案。
答案 0 :(得分:29)
nulls肯定比“神奇的价值”更好,即更诚实。但是在发生错误时不应该返回它们 - 这就是例外情况。当谈到返回集合时......我更赞同空集合而不是null,我同意。
答案 1 :(得分:11)
返回null很好,以下内容简洁易懂:
var session = GetUserSessionID(1) ?? GetNewUserSession(1);
答案 2 :(得分:8)
可能的做法是对未在找到结果时抛出异常的方法使用 get 前缀,如果可能为null,则使用 find 前缀。因此,在客户端很容易看到代码是否在处理null时遇到问题。
当然应该避免空值,Andrej Heljsberg在接受采访时说,如果现在创建C#,它将有更好的方法来处理可空性。 http://www.computerworld.com.au/article/261958/-z_programming_languages_c?pp=3&fp=&fpid=
答案 3 :(得分:5)
在我看来,你不应该排除使用null作为返回值。我认为它在许多情况下都是有效的。但是你应该仔细考虑每种方法的专业人士和骗子。这完全取决于方法的目的和呼叫者的期望。
在您可能期望该方法不返回abject(即主键的数据库搜索,可能只返回一个实例或none / null)的情况下,我个人非常使用null返回值。当您可以正确地期望方法返回值时,您应该使用异常。
在您的特定示例中,我认为这取决于系统的上下文。如果实例的调用仅来自您可能期望登录用户的代码,则应该抛出异常。但是,如果没有用户登录,因此您没有要返回的会话ID,则应选择返回null。
答案 4 :(得分:5)
为什么不使用Null design pattern?
答案 5 :(得分:4)
保持简单。让您的类型的消费者尽可能轻松。这是一个权衡:实施的时间和获得的好处。
如果您有更多类别,请发表评论,我会尝试解决方案。
答案 6 :(得分:3)
Nulls远比神奇的价值更好,更有意义。
您也可以尝试编写TryGetUserSession方法。
bool TryGetUserSession(int sessionId, out session)
还尝试不写nulls == ???,因为一些开发人员发现它更难阅读。
亲切的问候,
答案 7 :(得分:3)
我很担心自己会返回空值,但在你的例子中,这似乎是正确的做法。
重要的是清楚关于参数和返回值的可空性。如果您的语言无法直接表达此概念,则必须将 指定为API文档的一部分。 (Java不能,我已经读过C#可以。)如果输入参数或返回值 可以为null,请务必告诉用户中的意味着什么您的API的上下文。
我们最大的代码库是一个Java应用程序,在过去十年中稳步增长。许多内部API都非常不清楚他们的行为WRT null。由于每个人都在进行防御性编程,这导致了无处不在的空检的恶性增长。
特别难看:返回集合的函数返回null而不是空集合。 WTF!?不要那样做!
在字符串的情况下,请务必区分必须存在的字符串,但可以为空,并且字符串是真正可选的(可以为null)。在使用XML时,我们经常遇到这种区别:强制性的元素,但可以是空的,而不是可选的元素。
该系统的一部分提供了用于实现业务规则的类XML结构的查询,它非常激进地实现了无空闲。它到处使用Null对象模式。在这种情况下,这很有用,因为它允许你做以下事情:
A.children().first().matches("Blah")
如果A的第一个孩子被命名为“Blah”,那么这个表达式应该返回true。如果A没有子节点,它不会崩溃,因为在这种情况下,first()返回一个实现Node接口的NullNode,但从不“匹配”任何东西。
总结:
答案 8 :(得分:2)
一种解决方案可能是为这些事情声明一个简单的返回类型:
class Session
{
public bool Exists;
public string ID;
}
Session GetUserSession(int userID) {...}
...
Session session = GetUserSessionID(1);
if (session.Exists)
{
... use session.ID
}
我一般都喜欢避免空值,但字符串有点特殊。我经常最终调用string.IsNullOrEmpty()。这么多,我们使用字符串的扩展方法:
static class StringExtensions
{
public static bool IsNullOrEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
}
然后你可以这样做:
string s = ... something
if (s.IsNullOrEmpty())
{
...
}
此外,由于我们的主题是编码风格:
什么是不自然的“if(null == ...)”风格?这对于C#来说是完全不必要的 - 看起来有人带来了C#编码风格的一些C ++包袱!
答案 9 :(得分:2)
我更喜欢返回空集合而不是空值,因为这有助于避免使调用者代码混乱,如下所示:
if( list != null) {
foreach( Item item in list ) {
...
}
}
答案 10 :(得分:1)
按合同回答的设计也是我的解决方案:
if (myClass.SessionExists)
{
// Do something with myClass.Session
}
答案 11 :(得分:1)
我们总是返回空列表/集合,并且不返回NULL列表/集合,因为处理空集合/列表减少了调用函数的负担。
Nullable - 当你想避免处理数据库中的“null”值时,nullables特别有用。您可以简单地将GetDefaultOrValue用于任何Nullable类型,而无需担心业务对象中的数据库值为null。我们通常只在我们的业务对象中定义Nullables,并且我们真正想要转义db null值检查等等。
答案 12 :(得分:1)
nulls的发明者认为这是个坏主意。
答案 13 :(得分:1)
答案 14 :(得分:0)
NULL表示未知/未确定。
在使用数据库时,您最需要它,而数据库中的数据“尚未确定”。在您的应用程序中,NULL是非异常但非默认行为的良好返回类型。
当输出不是预期但对于异常而言不够严重时,使用NULL。