C# 从不使用空值?

C# 从不使用空值?,c#,null,C#,Null,我们目前正在经历为C#编写一些编码标准的漫长过程 我最近写了一个带有签名的方法 string GetUserSessionID(int UserID) 如果找不到用户的会话,则GetUserSession()返回null 在我的呼叫代码中。。。我说 string sessionID = GetUserSessionID(1) if (null == sessionID && userIsAllowedToGetSession) { session = GetNewUse

我们目前正在经历为C#编写一些编码标准的漫长过程

我最近写了一个带有签名的方法

string GetUserSessionID(int UserID)
如果找不到用户的会话,则GetUserSession()返回null

在我的呼叫代码中。。。我说

string sessionID = GetUserSessionID(1)
if (null == sessionID && userIsAllowedToGetSession)
{
    session = GetNewUserSession(1);
}
在最近的一次代码评审中,评审员说“您永远不应该从方法返回null,因为它会在调用方法上花费更多的工作来检查null。”

我马上喊出了诡计,好像你返回了字符串。空的话,你还得对返回的值进行某种检查

if (string.Empty == sessionID)
但是,进一步考虑一下,对于集合/数组/列表,我永远不会返回null。我将返回一个空列表

解决这个问题的方法(我认为)是将它重构为两个方法

bool SessionExists(int userID);

这次,GetUserSessionID将抛出SessionNotFound异常(因为它不应返回null)

现在代码看起来像

if(!SessionExists(1) && userIsAllowedToGetSession))
{
   session = GetNewUserSession(1);
}
else
{
   session = GetUserSessionID(1);
}
这意味着没有空值,但对我来说,这似乎有点复杂。这也是一个非常简单的例子,我想知道这将如何影响更复杂的方法

关于何时抛出异常以及如何处理异常,有很多最佳实践建议,但关于null的使用信息似乎较少

其他人是否有关于空值使用的可靠指导方针(甚至更好的标准),这对可空类型意味着什么(我们应该使用它们吗?)

提前感谢,

克里斯

=====

谢谢大家!那里有很多非常有趣的讨论。
我已经给出了egaga的答案,因为我喜欢Get vs Find作为编码指南的建议,但所有答案都很有趣。

空值肯定比“魔法值”更好,也就是说,更诚实。但当发生错误时,不应返回它们——这就是例外情况。当涉及到归还收藏时。。。我同意空集合比空集合好。

空表示未知/未确定

在处理数据只是“尚未确定”的数据库时,您最需要它。在您的应用程序中,对于非异常但非默认行为,NULL是一种很好的返回类型


当输出不是预期的但不足以导致异常时,使用NULL

一种可能的做法是使用get前缀作为未找到结果时抛出异常的方法,如果可能为null,则使用find前缀。因此,在客户端很容易看到代码在处理null时是否存在问题


当然,我们应该避免空值,安德烈·赫尔斯伯格(Andrej Heljsberg)在一次采访中说,如果C#现在被创建,它将有更好的方法处理空值性

保持简单。尽可能使您的类型的消费者无痛。这是一个权衡:实施的时间和获得的好处

  • 在处理诸如获取列表/集合之类的事情时,按照您正确指出的那样返回一个空列表
  • 类似地,对于需要返回“null”值以表示“未找到”的情况,请尝试对大量使用的类型使用。对于很少使用的类型,我想您可以接受一些null或-1(String.IndexOf)检查

如果您有更多类别,请以评论的形式发布,我将尝试解决方案。

一种解决方案可能是为以下内容声明一个简单的返回类型:

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语言编码风格中去了!p> 您的合同设计答案也是我的解决方案:

if (myClass.SessionExists)
{
   // Do something with myClass.Session
}

空值远比魔法值好,而且更有意义。 您还可以尝试编写一个TryGetUserSession方法。

bool TryGetUserSession(int sessionId,out session)

另外,尽量不要写null==?,因为一些开发人员发现它更难读取


亲切问候,

返回null很好,以下内容简洁易懂:

var session = GetUserSessionID(1) ?? GetNewUserSession(1);

在我看来,您不应该排除使用null作为返回值。我认为这在很多情况下都是有效的。但是你应该仔细考虑每一种方法的赞成和反对。这完全取决于方法的目的和调用方可能的期望

我个人经常使用null返回值,以防您期望该方法不会返回abject(即,通过主键进行的数据库搜索可能只返回一个实例,或者不返回/null)。如果可以正确地期望方法返回值,则应使用异常


在您的特定示例中,我认为这取决于系统的上下文。例如,如果调用仅来自您可能希望有登录用户的代码,那么您应该抛出一个异常。但是,如果可能没有用户登录,因此您没有要返回的会话id,那么您应该选择返回null。

我自己也担心返回null,尽管在您的示例中,这似乎是正确的做法

重要的是要清楚参数和返回值的可空性。如果您的语言无法直接表达此概念,则必须将其指定为API文档的一部分。(Java不能,我读过C#可以。)如果输入参数或返回值可以为null,请务必告诉用户在API上下文中这意味着什么

我们最大的代码库是Java应用程序,它在过去十年中稳步增长。许多内部API对其行为WRT null非常不清楚。这会导致nul癌变生长
var session = GetUserSessionID(1) ?? GetNewUserSession(1);
A.children().first().matches("Blah")
if( list != null) {
    foreach( Item item in list ) {
         ...
    }
}