最佳实践返回值、异常与枚举

本文关键字:异常 枚举 返回值 最佳 | 更新日期: 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.
}

或者,根据它的逻辑,使用不可变结构而不是类。 (确保结构是不可变的,可变结构只是在自找麻烦。 关键是您可以在结果对象本身上应用各种逻辑和功能,这似乎是您要前进的方向。

当然,这取决于某些情况,但让我在这里提供一些我的主观评论:

  1. 我相信这种情况应该避免。该方法的名称表明它只执行任何操作,如"登录"。根据方法名称,我们不能在这里期待任何结果。如果您希望该方法返回bool值,最好将其命名为IsLoggedIn(userName).此外,您永远不会知道是否需要扩展要返回的值集。因此,考虑到值的目的反映在enum名称而不是简单的bool中,这里的enum要好得多。

  2. 同上。此处的异常有助于停止整个执行层次结构(当然,调用堆栈中可以包含 1 个以上的方法),而不仅仅是返回结果并让调用方做出适当的决定。对我来说更灵活的解决方案。在当前情况下,我只会对参数验证使用异常。像"错误的用户名/密码"这样的情况并不例外。从用例的角度来看,它们是正常的。 null参数或错误的参数格式是例外情况。

  3. 如果您不需要该方法返回值,那就是要走的路。不要忘记,您不应该使用异常作为导航。我的意思是UserSuccessfullyCreatedException左右。

  4. 正如我上面提到的,这对我来说是最好的方法。单点是不要将验证异常作为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
}