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


当前回答

我同意这里的大多数帖子,它们趋向于零。

我的理由是,生成一个具有非空属性的空对象可能会导致错误。例如,具有int ID属性的实体的初始值为ID = 0,这是一个完全有效的值。如果这个对象,在某些情况下,被保存到数据库中,这将是一件坏事。

对于任何带有迭代器的东西,我总是使用空集合。类似的

foreach (var eachValue in collection ?? new List<Type>(0))

在我看来是代码的味道。集合属性永远不应该为空。

An edge case is String. Many people say, String.IsNullOrEmpty isn't really necessary, but you cannot always distinguish between an empty string and null. Furthermore, some database systems (Oracle) won't distinguish between them at all ('' gets stored as DBNULL), so you're forced to handle them equally. The reason for that is, most string values either come from user input or from external systems, while neither textboxes nor most exchange formats have different representations for '' and null. So even if the user wants to remove a value, he cannot do anything more than clearing the input control. Also the distinction of nullable and non-nullable nvarchar database fields is more than questionable, if your DBMS is not oracle - a mandatory field that allows '' is weird, your UI would never allow this, so your constraints do not map. So the answer here, in my opinion is, handle them equally, always.

Concerning your question regarding exceptions and performance: If you throw an exception which you cannot handle completely in your program logic, you have to abort, at some point, whatever your program is doing, and ask the user to redo whatever he just did. In that case, the performance penalty of a catch is really the least of your worries - having to ask the user is the elephant in the room (which means re-rendering the whole UI, or sending some HTML through the internet). So if you don't follow the anti-pattern of "Program Flow with Exceptions", don't bother, just throw one if it makes sense. Even in borderline cases, such as "Validation Exception", performance is really not an issue, since you have to ask the user again, in any case.

其他回答

在这种情况下,如果没有这样的用户,则最好返回“null”。还要使你的方法是静态的。

编辑:

通常这样的方法是一些“User”类的成员,不能访问它的实例成员。在这种情况下,方法应该是静态的,否则你必须创建一个“User”的实例,然后调用GetUserById方法,该方法将返回另一个“User”实例。我同意这很令人困惑。但是如果GetUserById方法是某个“DatabaseFactory”类的成员,那么将它作为实例成员是没有问题的。

在我们的业务对象中,我们有两个主要的Get方法:

为了让事情在语境中变得简单,或者你的问题是:

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

第一种方法用于获取特定的实体,第二种方法用于在网页上添加或编辑实体。

这使我们能够在使用它们的上下文中两全其美。

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

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

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

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

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

更多的肉要磨:让我们说我的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将是可接受的,因为这是数据访问逻辑。

另一方面,如果我希望我的参数反映一个主键,我应该只返回一行,如果我返回了不止一行,我就会抛出异常。0可以返回null, 2则不行。

现在,如果我有一些登录代码检查LDAP提供程序,然后检查DB以获得更多详细信息,并且我希望它们始终保持同步,那么我可能会抛出异常。正如其他人所说,这是商业规则。

Now I'll say that is a general rule. There are times where you may want to break that. However, my experience and experiments with C# (lots of that) and Java(a bit of that) has taught me that it is much more expensive performance wise to deal with exceptions than to handle predictable issues via conditional logic. I'm talking to the tune of 2 or 3 orders of magnitude more expensive in some cases. So, if it's possible your code could end up in a loop, then I would advise returning null and testing for it.