从函数返回数据的最佳实践是什么?是返回Null对象好还是返回空对象好?为什么要选择一种而不是另一种呢?

考虑一下:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

让我们假设在这个程序中存在有效的情况,即数据库中没有具有该GUID的用户信息。我认为在这种情况下抛出异常是不合适的??另外,我的印象是异常处理会损害性能。


当前回答

You should be throwing an exception if it is an exceptional circumstance that you call that code with an invalid user ID. If it is NOT an exceptional circumstance, then what you are essentially doing is using a "getter" method to test whether a user exists or not. That is like trying to open a file to see if it exists or not (lets stick to c#/java here) instead of using the exists method, or trying to access dictionary elements and seeing if they exist by looking at the return value instead of using the "contains" method first.

因此,您很可能会使用诸如“exists”之类的额外方法来首先检查是否存在这样的用户。异常的性能绝对不是完全不使用它们的理由,除非您有真正的性能问题。

其他回答

我不喜欢从任何方法返回null,而是使用Option函数类型。不返回结果的方法返回空选项,而不是null。

而且,不能返回结果的方法应该通过名称来指明。我通常把Try或TryGet或TryFind放在方法名称的开头,以表明它可能返回一个空结果(例如TryFindCustomer, TryLoadFile等)。

这让调用者可以对结果应用不同的技术,比如收集管道(参见Martin Fowler的收集管道)。

下面是另一个使用返回Option而不是null来降低代码复杂性的示例:如何降低圈复杂度:选项函数类型

这取决于什么对你的案子最有意义。

返回null是否有意义?“不存在这样的用户”?

或者创建一个默认用户有意义吗?当您可以安全地假设如果用户不存在,则调用代码在请求时希望用户存在时,这是最有意义的。

或者,如果调用代码要求使用无效ID的用户,抛出异常(如“FileNotFound”)是否有意义?

然而,从分离关注点/SRP的角度来看,前两点更为正确。从技术上讲,第一个是最正确的(但只差一点点)——GetUserById应该只负责一件事——获取用户。通过返回其他内容来处理自己的“用户不存在”情况可能违反SRP。如果您确实选择抛出异常,则分隔为不同的check - bool DoesUserExist(id)是合适的。

根据下面大量的评论:如果这是一个api级别的设计问题,这个方法可以类似于“OpenFile”或“readwholfile”。我们正在从某个存储库中“打开”一个用户,并从结果数据中补充对象。在这种情况下,一个例外可能是合适的。也许不是,但也有可能。

所有的方法都是可以接受的——这取决于API/应用程序的上下文。

(仅)在特定契约被破坏时抛出异常。 在您的特定示例中,请求基于已知Id的UserEntity,这将取决于丢失(删除)用户是否是预期情况。如果是,则返回null,但如果不是预期的情况,则抛出异常。 注意,如果函数名为UserEntity GetUserByName(字符串名),它可能不会抛出而是返回null。在这两种情况下,返回空的UserEntity都没有帮助。

对于字符串、数组和集合,情况通常是不同的。我记得MS的一些指导原则,方法应该接受null作为一个“空”列表,但返回零长度的集合而不是null。字符串也是一样。注意,你可以声明空数组:int[] arr = new int[0];

为了代码库的健康,我认为函数不应该返回null。我能想到几个原因:

将有大量的保护子句处理空引用if (f() != null)。

什么是空,它是一个公认的答案还是一个问题?null是特定对象的有效状态吗?(假设您是代码的客户端)。我的意思是所有引用类型都可以为空,但是它们应该为空吗?

当你的代码库不断增长时,使用null几乎总是会不时地产生一些意想不到的NullRef异常。

有一些解决方案,测试者-执行者模式或从函数式编程实现选项类型。

我会说返回null而不是空对象。

但你在这里提到的具体例子, 您正在通过用户id搜索用户,即排序 那个用户的键,在这种情况下,我可能想要 如果没有用户实例,则抛出异常 发现。

这是我通常遵循的规则:

如果通过主键查找操作没有找到结果, 把ObjectNotFoundException。 如按任何其他标准没有发现结果, 返回null。 如果通过非关键条件查找未找到结果,则可能返回多个对象 返回一个空集合。