【问题标题】:Best Practice Return Value vs Exception vs Enum [closed]最佳实践返回值与异常与枚举[关闭]
【发布时间】:2014-04-02 09:43:56
【问题描述】:

我正在尝试找出具有多个结果值的方法的优缺点。

例如,我正在使用登录方法。如果登录成功则通过,否则我需要知道失败的原因。

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);

“登录”只是一个例子。我想知道不同实现的优缺点是什么,以及它们或多或少适合哪些情况。

【问题讨论】:

  • 1 和 2 显然不值得考虑。 #3 与通常的做事方式一致,但唯一能够认识到 #4 可能更适合您的情况的人是您。
  • API 是同步的还是异步的?
  • 同步的,抱歉 :)
  • “异常是针对异常错误”这句话应该指导您对抗 #2 或 #3。 #4 在可能有多种失败原因的情况下是可以接受的,而在只有 I 原因失败的情况下 #1 是可以接受的。在我自己的库中,我创建了一个类 ResultString,其中包含一个用于成功/失败的布尔值和一个用于向用户显示详细信息的字符串。这允许函数确定故障的最佳描述。

标签: c# exception login enums


【解决方案1】:

绝对不是例外。登录失败几乎不是“例外”情况,它只是应用程序的正常逻辑过程。如果您使用异常,那么除了处理登录失败的情况外,您将始终必须使用异常处理程序包装登录。这似乎是对逻辑流使用异常的定义,这是不对的。

如果您需要返回特定信息(登录功能并非总是必需,但可能是您的情况),#4 似乎是合理的。你可以更进一步,让它成为一个对象:

public class LoginResult
{
    // an enum for the status
    // a string for a more specific message
    // a valid user object on successful login
    // etc.
}

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

【讨论】:

    【解决方案2】:

    当然这取决于特定的情况,但让我在这里提供一些我的主观cmet:

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

    2. 同上。这里的异常有助于停止整个执行层次结构(它当然可以在调用堆栈中包含多个方法),而不是仅仅返回结果并让调用者做出适当的决定。对我来说更灵活的解决方案。在当前情况下,我只会将异常用于参数验证。 “错误的用户名/密码”等情况也不例外。从用例的角度来看,它们是正常的。 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】:

      你会得到更多固执己见的答案,如果我这样做,我会结合 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 会当面抛出异常,说由于某某原因,我无法处理您的登录请求。让它变得简单。

      【讨论】:

      • 我喜欢这种方法,尽管在 imho 似乎很常见的情况下抛出异常感觉不对。我觉得应该在更深层次上出现问题的“异常”场景中使用异常。
      • @Moeri 我相信这是正确的方法,登录失败不是一种常见的情况,它是一个异常的情况,考虑到你选择的只是一个 Enum 方法,你开发一个公共 API,如果用户不检查返回值而只是调用 Login(); 并假设已登录怎么办?扔它面对说嘿,出了点问题(就像 sqlserver 通过 SqlException 做的一样)
      • @Moeri 例外是替换C-Style 状态码和GetLastError() 的。你将如何用这种方法实现Inner Exception 的想法? 两层或多层外发生错误的情况
      【解决方案4】:

      我通常在我的项目中使用这种方法:

      签名:

      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
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-13
        • 1970-01-01
        • 1970-01-01
        • 2020-05-09
        • 2010-09-07
        • 2013-11-09
        相关资源
        最近更新 更多