从函数返回数据的最佳实践是什么?是返回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的用户信息。我认为在这种情况下抛出异常是不合适的??另外,我的印象是异常处理会损害性能。
我们使用CSLA。NET,并且它认为失败的数据获取应该返回一个“空”对象。这实际上是相当烦人的,因为它要求检查obj。IsNew而不是obj == null。
正如前面提到的,null返回值将导致代码立即失败,从而降低了由空对象引起的隐形问题的可能性。
就我个人而言,我认为null更优雅。
这是一种非常常见的情况,我很惊讶这里的人似乎对此感到惊讶:在任何web应用程序中,数据通常是使用querystring参数获取的,这显然会被破坏,因此要求开发人员处理“未找到”的情况。
你可以这样处理:
if (User.Exists(id)) {
this.User = User.Fetch(id);
} else {
Response.Redirect("~/notfound.aspx");
}
...但是这每次都是对数据库的额外调用,这在高流量页面上可能是一个问题。而:
this.User = User.Fetch(id);
if (this.User == null) {
Response.Redirect("~/notfound.aspx");
}
...只需要一个呼叫。
对于集合类型,我将返回一个空集合,对于所有其他类型,我更喜欢使用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将是可接受的,因为这是数据访问逻辑。
另一方面,如果我希望我的参数反映一个主键,我应该只返回一行,如果我返回了不止一行,我就会抛出异常。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.
如果用户没有被找到的情况经常出现,你想要根据情况以各种方式处理(有时抛出异常,有时替换一个空用户),你也可以使用接近f#的Option或Haskell的Maybe类型,它显式地将“无值”情况与“发现了一些东西!”数据库访问代码看起来像这样:
public Option<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 Option<UserEntity>.Nothing;
else
return Option.Just(existingUserEntity);
}
并且像这样使用:
Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
// deal with it
} else {
UserEntity value = result.GetValue();
}
不幸的是,每个人似乎都有自己喜欢的类型。