【问题标题】:Returning values, errors and status codes返回值、错误和状态码
【发布时间】:2012-03-14 20:34:35
【问题描述】:

我正在反对古老的返回码与异常的辩论,并且可以针对具体示例提出一些建议。

我有一个简单的方法 Authenticate,它使用你的典型用户名、密码并在成功时返回会话密钥,否则返回失败原因。比如:

Guid Authenticate(string username, string password)

虽然不会向用户显示失败原因,但我想将其向上传递,以便他们采取适当的措施。

我认为有三组可能的反应:
1. 成功
2. 预期/常见故障,例如密码/用户无效,帐户被锁定
3. 异常/意外故障/异常,例如数据库连接失败等

我很高兴通过返回有效的 guid 来指示 1,通过冒泡的异常指示 3,但是我应该为 2 做什么?

我正在接近例外情况,但我不确定像密码错误这样的常见问题,这种情况可能在 50% 的时间发生,应该这样做吗?替代方案正在返回某种复杂的返回状态对象(如果成功,则包括 guid)或 out 参数?

【问题讨论】:

  • 如果失败是意料之中的,那么在我看来,异常似乎不是可行的方法,因为它们不是异常的,而且是您可以处理的。

标签: c#


【解决方案1】:

您可以使用out 返回一个枚举,如果成功,将设置 Guid,否则您可以检查它的状态:

Guid Authenticate(string username, string password, out AuthResult authResult)

enum AuthResult {
    Success,
    BadPassword,
    BadUserName,
    .... 
}

或者,稍微扭转一下逻辑 - 从方法中返回一个 Enum,并通过 out 设置 GuiD

AuthResult Authenticate(string username, string password, out Guid guid)

【讨论】:

    【解决方案2】:

    Out 变量,可以解决问题,您可以使用 Out 变量Out Parameters

    Guid Authenticate(string username, string password, out bool Status)
    {
       Status=false; //initially it will be false 
      if(LoginSuccess)
      {
        Status=True; // So that you can check whether the user is valid or not 
      }
    
    }
    

    像这样调用这个函数

    bool LoginStatus; 
    someGuidObj =  Authenticate(username, password, out LoginStatus)
    

    【讨论】:

      【解决方案3】:

      您知道您可以使用不同类型的异常,具体取决于您计划如何处理它们。我建议将ApplicationException 用于您希望向用户显示的那些异常,而将通用Exception 用于您不想显示的其他内容。

      我强烈反对 C# 等不支持联合类型的语言中的特殊返回值。特殊返回值在 Haskell 等语言中正常工作,否则它们总是成为求助于文档和错误来源的理由。

      【讨论】:

      • ApplicationException 不应再使用。可以使用直接从Exception 派生的异常。 stackoverflow.com/questions/5685923/…
      • @Firedragon 是的,你是对的。自从我上次使用 C# 以来已经有一段时间了。在任何情况下,用户定义的特殊异常类与ApplicationException 的工作方式类似,就是答案。
      【解决方案4】:

      尝试使用模式Do()/TryDo() 以获得更好的 API 灵活性。因此,使用此 API 的客户端(类、服务等)将具有以下选项:

      API:

      // Throw in case of error with AuthenticateExceptionEventArgs 
      // contains information regarding error context
      Guid Authenticate(string name, string password);
      
      // Returns a status of operation, does not throw in case of error
      AuthenticateStatus TryAuthenticate(string name, string password, out Guid guid);
      

      需要以下基础设施类型:

      enum AuthenticateStatus
      {  
         Success,
         InvalidPassword
      }
      
      class AuthenticateExceptionEventArgs : EventArgs
      {
          public AuthenticateStatus Status { get; private set; }
      }
      

      【讨论】:

      • 是否有充分的理由在这里同时使用这两种方法,因为我认为这两种方法都需要进行异常处理,只有一个自己处理,另一个你需要做?
      • @Firedragon : 两种方法是为了更好地提高 API 的灵活性,所以如果你想使用更轻量级/更快的方式 - 使用不会产生异常的方法,否则你显然需要用 Authenticate() 包装try/catch 块,由 API 客户端决定
      【解决方案5】:

      返回自定义类型作为结果并且不抛出任何异常:

      public class AuthenticationResult
      {
          // will be Guid.Empty if unable to authenticate.
          public Guid SessionKey
          {
              get;
              set;
          }
      
          public bool Authenticated
          {
              get
              {
                  return this.SessionKey != Guid.Empty
              }
          }
      
          // This will contain the login failure reason.
          public string FailureMessage
          {
              get;
              set;
          }
      }
      

      如果需要,调用 Authenticate 方法的代码可以抛出异常并显示失败消息。

      【讨论】:

        【解决方案6】:

        异常的默认动作是程序死掉;返回码的默认操作是让程序继续。例如,如果该方法有一个无效密码,它是死了还是继续下去会更好吗?这将是您确定是否应该使用异常或返回代码的标准。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-26
          • 1970-01-01
          • 2012-07-07
          • 2016-04-16
          • 2016-09-14
          相关资源
          最近更新 更多