最佳实践返回值、异常与枚举
本文关键字:异常 枚举 返回值 最佳 | 更新日期: 2023-09-27 17:56:13
我试图找出具有多个结果值的方法的优缺点。
例如,我正在使用登录方法。如果登录成功,它将通过,否则我需要知道它失败的原因。
1. 返回真或假(信息不足)
bool Login(string user, string password);
2. 如果成功,则返回 true,否则抛出异常
public class UnknownUserException : Exception { }
public class WrongPasswordException : Exception { }
bool Login(string user, string password);
3. 什么都不返回。如果不成功,则引发异常
public class UnknownUserException : Exception { }
public class WrongPasswordException : Exception { }
void Login(string user, string password);
4. 返回枚举值
enum LoginResult
{
Successful
UnknownUser,
WrongPassword
}
LoginResult Login(string user, string password);
"登录"只是一个示例案例。我想知道不同实现的优点和缺点是什么,以及它们或多或少适合哪些情况。
绝对不是例外。 登录失败几乎不是"例外"情况,它只是应用程序的正常逻辑过程。 如果使用异常,则始终必须使用异常处理程序包装登录,原因无他,只是为了处理登录失败的情况。 这似乎是对逻辑流使用异常的定义,这是不对的。
如果您需要返回特定信息(登录函数并不总是必需的,但可能是在您的情况下),#4 似乎是合理的。 你可以更进一步,让它成为一个对象:
public class LoginResult
{
// an enum for the status
// a string for a more specific message
// a valid user object on successful login
// etc.
}
或者,根据它的逻辑,使用不可变结构而不是类。 (确保结构是不可变的,可变结构只是在自找麻烦。 关键是您可以在结果对象本身上应用各种逻辑和功能,这似乎是您要前进的方向。
当然,这取决于某些情况,但让我在这里提供一些我的主观评论:
-
我相信这种情况应该避免。该方法的名称表明它只执行任何操作,如"登录"。根据方法名称,我们不能在这里期待任何结果。如果您希望该方法返回
bool
值,最好将其命名为IsLoggedIn(userName)
.此外,您永远不会知道是否需要扩展要返回的值集。因此,考虑到值的目的反映在enum
名称而不是简单的bool
中,这里的enum
要好得多。 -
同上。此处的异常有助于停止整个执行层次结构(当然,调用堆栈中可以包含 1 个以上的方法),而不仅仅是返回结果并让调用方做出适当的决定。对我来说更灵活的解决方案。在当前情况下,我只会对参数验证使用异常。像"错误的用户名/密码"这样的情况并不例外。从用例的角度来看,它们是正常的。
null
参数或错误的参数格式是例外情况。 -
如果您不需要该方法返回值,那就是要走的路。不要忘记,您不应该使用异常作为导航。我的意思是
UserSuccessfullyCreatedException
左右。 -
正如我上面提到的,这对我来说是最好的方法。单点是不要将验证异常作为
enum
值放置。你有例外。
因此enum
结果加上验证的例外是一种方法。
如果要收集方法执行期间的所有错误,则可能需要创建特殊的LoginOperationResult
类来包装所有信息(包括方法执行期间发生的验证错误)。
class OperationResult
{
public OperationStatus Status { get; set; }
public IEnumerable<ValidationError> Errors { get; set; }
// or list of exceptions
}
class LoginOperationResult : OperationResult
{
// Login result specific data.
}
enum OperationStatus
{
Success,
Denied,
ValidationFailed,
// etc.
}
得到更多固执己见的答案,如果我这样做,我会结合3和4。用枚举LoginFailedException
说为什么。
void Login(string user, string password);//Or return a bool(redundant though)
class LoginFailedException : ApplicationException
{
public LoginFailReason Reason {get; private set;}
public LoginFailedException(LoginFailReason reason)
{
this.Reason = reason;
}
}
enum LoginFailReason
{
UnknownUser,
WrongPassword
}
选择例外选项的原因:假设你选择仅返回值的方法,你的 API 的用户(可能是客户端或其他开发人员)有机会忽略 API。
instance.Login(user, password);
var accountInfo = instance.GetAccountInfo();//Assuming logged in; going to explode
谁知道他们必须这样做
if(instance.Login(user, password) == LoginResult.Successful))
{
var accountInfo = instance.GetAccountInfo();
}
因此,IMO在脸上抛出异常,说由于某某原因,我无法处理您的登录请求。让它变得简单。
我通常在项目中使用这种方法:
签名:
bool TryLogin(string username, string password, out User user);
用法:
User user;
if(userService.TryLogin(username, password, out user)))
{
// do stuff with user
}
else
{
// show "login failed"
}
您可以展开它以返回您的枚举:
签名:
enum LoginResult
{
Successful
UnknownUser,
WrongPassword
}
LoginResult TryLogin(string username, string password, out User user);
用法:
User user;
LoginResult loginResult;
if((loginResult = userService.TryLogin(username, password, out user)) == LoginResult.Successful)
{
// do stuff with user
}
else
{
// do stuff with loginResult
}