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


当前回答

异步TryGet模式:

对于同步方法,我相信@Johann Gerell的回答是适用于所有情况的模式。

但是带有out参数的TryGet模式不适用于Async方法。

使用c# 7的元组文字,你现在可以这样做:

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}

其他回答

我倾向于

如果对象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,因为这是我所期望的DAL/Repository层的操作方式。

如果它不存在,不要返回任何可以被解释为成功获取对象的东西,null在这里工作得很好。

最重要的是在你的DAL/Repos层保持一致,这样你就不会对如何使用它感到困惑。

对于集合类型,我将返回一个空集合,对于所有其他类型,我更喜欢使用NullObject模式来返回实现与返回类型相同接口的对象。有关该模式的详细信息,请查看链接文本

使用NullObject模式这将是:-

public UserEntity GetUserById(Guid userId)

{ //假设这里有一些代码来访问数据库.....

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

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 

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

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

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

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

就我个人而言,我使用NULL。它清楚地表明没有数据要返回。但在某些情况下,空对象可能是有用的。