从函数返回数据的最佳实践是什么?是返回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的用户信息。我认为在这种情况下抛出异常是不合适的??另外,我的印象是异常处理会损害性能。


当前回答

我通常返回null。它提供了一种快速而简单的机制来检测是否出现了错误,而不会抛出异常,也不会到处使用大量的try/catch。

其他回答

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”之类的额外方法来首先检查是否存在这样的用户。异常的性能绝对不是完全不使用它们的理由,除非您有真正的性能问题。

这是一个业务问题,取决于具有特定Guid Id的用户的存在是否是该函数的预期正常用例,还是会阻止应用程序成功完成该方法向用户对象提供的任何功能的异常情况……

如果它是一个“异常”,因为缺少具有该Id的用户将阻止应用程序成功完成它正在做的任何功能,(假设我们正在为我们已向其发货的客户创建发票……),那么这种情况应该抛出ArgumentException(或其他一些自定义异常)。

如果没有用户(调用此函数的潜在正常结果之一),则返回空值....

编辑:(处理亚当在另一个回答中的评论)

If the application contains multiple business processes, one or more of which require a User in order to complete successfully, and one or more of which can complete successfully without a user, then the exception should be thrown further up the call stack, closer to where the business processes which require a User are calling this thread of execution. Methods between this method and that point (where the exception is being thrown) should just communicate that no user exists (null, boolean, whatever - this is an implementation detail).

但是如果应用程序中的所有进程都需要一个用户,我仍然会在这个方法中抛出异常……

还有一种方法涉及传入一个回调对象或委托,它将对值进行操作。如果没有找到值,则不调用回调。

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

当您希望避免在整个代码中进行空检查时,以及当找不到值并不是错误时,这种方法非常有效。如果需要任何特殊处理,还可以在没有找到对象时提供回调。

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

使用单个对象的相同方法如下所示:

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

从设计的角度来看,我真的很喜欢这种方法,但是它的缺点是在不支持第一类函数的语言中使调用站点变得更庞大。

我倾向于

如果对象id不存在,在不知道它是否应该存在时返回null。 当对象id应该存在时,如果对象id不存在则抛出。

我用这三种类型的方法来区分这两种场景。 第一:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

第二:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

第三:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}

我通常返回null。它提供了一种快速而简单的机制来检测是否出现了错误,而不会抛出异常,也不会到处使用大量的try/catch。