C# 函数应该返回null还是空对象?
从函数返回数据时的最佳实践是什么。返回Null还是空对象更好?为什么一个人要做一件事而不是另一件 考虑这一点:C# 函数应该返回null还是空对象?,c#,.net,function,return-value,C#,.net,Function,Return Value,从函数返回数据时的最佳实践是什么。返回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)
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通常是最好的主意 空对象表示已返回数据,而返回null则明确表示未返回任何内容
此外,如果试图访问对象中的成员,返回null将导致null异常,这对于突出显示错误代码非常有用-尝试访问空成员没有任何意义。访问空对象的成员不会失败,这意味着bug可能无法被发现。这取决于什么对您的情况最有意义 返回null是否有意义,例如“不存在这样的用户” 或者创建默认用户有意义吗?当您可以安全地假设用户不存在时,调用代码希望用户在请求时存在时,这是最有意义的 或者,如果调用代码要求用户使用无效ID,抛出异常(la“FileNotFound”)是否有意义 然而,从关注点分离/SRP的角度来看,前两个更为正确。从技术上讲,第一个是最正确的(但只需要一根头发)——GetUserById应该只负责一件事——获取用户。通过返回其他内容来处理自己的“用户不存在”案例可能违反SRP。如果您选择抛出异常,则将其分离为不同的检查-
bool doesurexist(id)
是合适的
基于下面的大量评论:如果这是一个API级设计问题,此方法可能类似于“OpenFile”或“ReadEntRefile”。我们正在从某个存储库“打开”一个用户,并从生成的数据中水合该对象。在这种情况下,例外情况可能是合适的。可能不是,但可能是
所有方法都是可以接受的——这取决于API/应用程序的更大上下文 我个人会返回null,因为我希望DAL/存储库层会这样做 如果它不存在,不要返回任何可能被解释为成功获取对象的内容,
null
在这里工作得很好
最重要的是在DAL/Repos层上保持一致,这样您就不会对如何使用它感到困惑。这是一个业务问题,取决于具有特定Guid Id的用户是否是此功能的预期正常用例,或者这是一种异常现象,它会阻止应用程序成功完成此方法向用户对象提供的任何功能 如果它是一个“异常”,即缺少具有该Id的用户将阻止应用程序成功完成其正在执行的任何功能(例如,我们正在为我们将产品运送到的客户创建发票…),那么这种情况应该引发ArgumentException(或其他一些自定义异常) 如果缺少的用户是ok(调用此函数的潜在正常结果之一),则返回null 编辑:(在另一个答案中回答亚当的评论) 如果应用程序包含多个业务流程,其中一个或多个业务流程需要用户才能成功完成,并且其中一个或多个业务流程可以在没有用户的情况下成功完成,则应在调用堆栈的更上层引发异常,更接近需要用户的业务流程调用此执行线程的位置。这个方法和那个点(抛出异常的地方)之间的方法应该只传递不存在用户的信息(null、boolean等等,这是一个实现细节)
但是如果应用程序中的所有进程都需要一个用户,我仍然会在这个方法中抛出异常 它会根据上下文而变化,但如果我在寻找一个特定对象(如您的示例中),通常会返回null,如果我在寻找一组对象但没有,则返回空集合
如果您在代码中犯了一个错误,并且返回null会导致null指针异常,那么越早发现越好。如果返回一个空对象,最初使用它可能有效,但以后可能会出现错误。我个人使用NULL。它清楚地表明没有要返回的数据。但在某些情况下,a可能是有用的。我通常返回null。它提供了一种快速而简单的机制,可以检测出是否有问题,而不会抛出异常,也不会到处使用大量的try/catch。如果返回类型是数组,则返回空数组,否则返回null。在这种情况下,如果没有这样的用户,最好返回“null”。还要使您的方法保持静态 编辑:
通常,像这样的方法是某些“用户”类的成员,没有访问其实例成员的权限。在这种情况下,该方法应该是静态的,否则必须创建一个“User”实例,然后调用GetUserById方法,该方法将返回另一个“User”实例。我同意这令人困惑。但是,如果GetUserById方法是某个“DatabaseFactory”类的成员,那么将其保留为实例成员是没有问题的。如果某个特定的约定被破坏,您应该(仅)抛出一个异常。
在您的特定示例中,根据已知Id请求UserEntity,这将取决于缺少(删除)用户是否为预期情况。如果是,则返回
null
,但如果是
// 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)
{
}
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 ""; } ...}
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");
}
function saveTheRow($prim_key, $data) {
$row = getRowByPrimKey($prim_key);
// Populate the data here
$row->save();
}
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
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();
}
bool IsAdministrator(User user)
{
var groupsOfUser = GetGroupsOfUser(user);
// This foreach would cause a run time exception if groupsOfUser is null.
foreach (var groupOfUser in groupsOfUser)
{
if (groupOfUser.Name == "Administrators")
{
return true;
}
}
return false;
}
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();
}
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;
}
foreach (var eachValue in collection ?? new List<Type>(0))
async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
if (InternalIdExists(id))
{
o = await InternalGetSomeObjectAsync(id);
return (true, o);
}
else
{
return (false, default(SomeObject));
}
}