从函数返回数据的最佳实践是什么?是返回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的用户信息。我认为在这种情况下抛出异常是不合适的??另外,我的印象是异常处理会损害性能。
更多的肉要磨:让我们说我的DAL返回一个NULL的GetPersonByID,正如一些建议。我的(相当薄)BLL应该做什么,如果它收到一个NULL?传递NULL,并让最终消费者担心它(在这种情况下,一个ASP。网络页面)?让BLL抛出一个异常怎么样?
BLL可能正在被ASP使用。Net和Win App,或者其他类库——我认为期望最终消费者本质上“知道”GetPersonByID方法返回null是不公平的(除非使用空类型,我猜)。
My take (for what it's worth) is that my DAL returns NULL if nothing is found. FOR SOME OBJECTS, that's ok - it could be a 0:many list of things, so not having any things is fine (e.g. a list of favourite books). In this case, my BLL returns an empty list. For most single entity things (e.g. user, account, invoice) if I don't have one, then that's definitely a problem and a throw a costly exception. However, seeing as retrieving a user by a unique identifier that's been previously given by the application should always return a user, the exception is a "proper" exception, as in it's exceptional. The end consumer of the BLL (ASP.Net, f'rinstance) only ever expects things to be hunky-dory, so an Unhandled Exception Handler will be used instead of wrapping every single call to GetPersonByID in a try - catch block.
如果我的方法有明显的问题,请让我知道,因为我总是渴望学习。正如其他帖子所说,异常是代价高昂的事情,“先检查”的方法是好的,但异常应该只是例外。
我很喜欢这篇文章,很多关于“视情况而定”的好建议:-)
为了代码库的健康,我认为函数不应该返回null。我能想到几个原因:
将有大量的保护子句处理空引用if (f() != null)。
什么是空,它是一个公认的答案还是一个问题?null是特定对象的有效状态吗?(假设您是代码的客户端)。我的意思是所有引用类型都可以为空,但是它们应该为空吗?
当你的代码库不断增长时,使用null几乎总是会不时地产生一些意想不到的NullRef异常。
有一些解决方案,测试者-执行者模式或从函数式编程实现选项类型。
我倾向于
如果对象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,而是使用Option函数类型。不返回结果的方法返回空选项,而不是null。
而且,不能返回结果的方法应该通过名称来指明。我通常把Try或TryGet或TryFind放在方法名称的开头,以表明它可能返回一个空结果(例如TryFindCustomer, TryLoadFile等)。
这让调用者可以对结果应用不同的技术,比如收集管道(参见Martin Fowler的收集管道)。
下面是另一个使用返回Option而不是null来降低代码复杂性的示例:如何降低圈复杂度:选项函数类型