【问题标题】:Best Practice for Try Catch Error HandlingTry Catch 错误处理的最佳实践
【发布时间】:2011-03-26 12:17:28
【问题描述】:

我试图避免在捕获时返回不正确的值,但我找不到比这更好的解决方案:

    private SecurityLevel ApiGetSecurityLevel()
    {
        try
        {
            return _BioidInstance.GetSecurityLevel();
        }
        catch
        { 
            return SecurityLevel.High;
        }
    }

有没有更好的方法来避免返回不正确的值?我无法更改 SecurityLevel 枚举。

【问题讨论】:

  • 请稍微解释一下在捕获异常时什么是“正确值”?
  • 您在 _BioidInstance 中的安全级别是否可以设置为除了 SecurityLevel 枚举中的内容之外的任何其他内容?
  • 这是一个致命的例程。它应该是故障安全的:返回 SecurityLevel.None;
  • 您希望 GetSecurityLevel() 调用抛出哪些类型的异常?

标签: .net exception exception-handling


【解决方案1】:

不要捕获异常。允许“冒泡”异常以强制调用者/调用者处理设置默认安全值。


如果您真的想返回一个值,请使用Nullable<SecurityLevel>SecurityLevel?

private SecurityLevel? ApiGetSecurityLevel() { 
    try { 
        return _BioidInstance.GetSecurityLevel(); 
    } 
    catch {  
        return null; 
    } 
} 

然后用作:

if (ApiGetSecurityLevel().HasValue == false) {
    // use default security level
}

【讨论】:

  • 假设他想在 if 中验证它的值之前缓存返回,否则他将不得不再次运行它来检索可能意味着另一个 DB 命中的值。
  • @Jimmy Hoffa,不明白你的评论。什么数据库命中?什么指示让您觉得 OP 想要缓存返回?
  • 如果他不想缓存方法的返回,他会让方法返回true或false,而不是一个可识别的值。
  • @Jimmy Hoffa:我觉得很傻,但我还是不明白你的意思。他为什么要缓存这个值?该方法显然用于返回安全级别,然后将其与SecurityLevel 枚举进行比较。
【解决方案2】:

这可能是应用程序失败的情况吗?也就是说,如果无法确定SecurityLevel,用户应该不能继续?

如果是这样,为什么不直接重新抛出并让 UI 处理它(或者让它被记录,但是你的商店可以工作)?

如果应用程序应该能够继续,选择(并记录)一些默认值并完全按照您正在做的事情。

【讨论】:

    【解决方案3】:

    首先,只要GetSecurityLevel 返回SecurityLevel,就没有理由尝试/捕获。编译器会在那里发现任何问题。

    其次,这不是 try/catch 的好用处。 Try/catch 永远不应该用于正常的控制流,只用于例外情况。

    如果由于某种原因,GetSecurityLevel() 没有返回 SecurityLevel 枚举类型:

    private SecurityLevel ApiGetSecurityLevel()
        {
            object securityLevel = _BioidInstance.GetSecurityLevel();
            if (securityLevel is SecurityLevel)
            {
                 return _BioidInstance.GetSecurityLevel();
            }
            else
            {
                 throw new Exception("Invalid SecurityLevel");
            }
        }
    

    【讨论】:

      【解决方案4】:

      如果你可以更改函数的返回类型,我会将其更改为可为空的枚举并在 catch 中返回 null。

      private SecurityLevel? ApiGetSecurityLevel()
      {
          try
          {
              return _BioidInstance.GetSecurityLevel();
          }
          catch
          { 
              return null;
          }
      }
      

      【讨论】:

      • 所有这一切都是将异常向上推:当他们取消引用该值时它会抛出。
      【解决方案5】:

      你可以返回 Nothing。并让您的函数将结果视为 If NOT ApiGetSecurityLevel() Is Nothing

      【讨论】:

      • 是的。这是对 catch 块的滥用。它们用于捕获特定的异常。但是,请尝试使用 IsNot 而不是 Not object IsNothing。
      • 他说 SecurityLevel 是一个枚举,所以 Nothing(C# 中的null)不是有效值。
      • @Justin Nothing 在这种情况下相当于 C# 中的 default(SecurityLevel)
      【解决方案6】:

      你可以让异常冒出来。

      【讨论】:

      • 或者,更好的是,捕获它并作为AuthorizationException 的内部异常重新抛出。无论哪种方式,失败都会出现异常,而不是返回值。
      【解决方案7】:

      除了马特的回答之外,让异常发生有什么问题?如果您的应用程序中存在一些无效的状态或访问权限,最好直接杀死它。

      【讨论】:

        【解决方案8】:
        public class BioId { public SecurityLevel SecLevel { get; set; } }
        
        private bool ApiGetSecurityLevel(BioId bioId)
        {
            try
            {
                bioIdSecLevel = bioId.GetSecurityLevel();
                return true;
            }
            catch (PossibleException)
            {
                return false;
            }
        }
        

        然后将它与 if 一起使用,如果它返回 false,则让处理代码决定其 BioId 的默认安全级别。

        虽然我同意其他所有人的观点,但实际上你只想让异常冒泡并让顶层处理它们。虽然有时有一些标准不允许异常冒泡,因为它们冒泡的每一层显然都会影响性能,但我说你不应该有太多的层,洋葱很复杂,就像食人魔一样。

        【讨论】:

        • 只有在特殊情况下才会抛出异常,也就是说,不是很频繁。因此,如果使用得当,它们对性能没有影响。
        • 无论如何,这是一个非答案,因为他们需要返回该级别,而不是布尔值。也许您想将其作为输出参数并使用 Try 隐喻。
        【解决方案9】:

        如果您可以将Nothing/NotSet 枚举添加到SecurityLevel,那么您可以从 catch 块中返回它。在您无法确定升级的 priv 时返回它们是一个非常奇怪的选择。

        或者,将 SecurityLevel 作为可为空的枚举返回。

        private SecurityLevel? ApiGetSecurityLevel()
        {
            try
            {
                return _BioidInstance.GetSecurityLevel();
            }
            catch
            { 
                return null;
            }
        }
        

        【讨论】:

          【解决方案10】:
          SecurityLevel sec = _BioidInstance.GetSecurityLevel();
          return (Enum.IsDefined(Typeof(SecurityLevel), sec) ? sec : SecurityLevel.High);
          

          如果在枚举中定义,则返回_BioidInstance中的安全级别,否则返回SecurityLevel.High。

          如果是我,如果我知道安全级别不能超出枚举中定义的级别,我会不使用 try-catch 并让它失败。这样我就知道其他地方出了问题,我可以去修复它。

          【讨论】:

            【解决方案11】:

            在这种情况下,安全性变成了二进制。

            举一个安全大院的例子,在大门口有人可以进入,也可以不可以。如果他们未能通过安全级别,则该人不应通过大门。

            如果 GetSecurityLevel() 方法抛出异常,则说明某些东西在门口被拒之门外。然后授予一些任意的安全级别并允许它们通过不是一个好主意。

            我会完全取消这种方法,并用更接近的方法替换它

            private bool HasSecurityLevel(SecurityLevel securityLevel) 
            { 
                try 
                { 
                    return _BioidInstance.GetSecurityLevel() == securityLevel; 
                } 
                catch 
                {  
                    return false; 
                } 
            } 
            

            然后根据需要检查每个安全级别。

            这样做的理由是在某些时候必须进行检查,最好尽可能靠近源头(当第一次获得安全级别时)。

            【讨论】:

              猜你喜欢
              • 2013-12-28
              • 1970-01-01
              • 2021-10-02
              • 1970-01-01
              • 2018-05-29
              • 2013-02-05
              • 2018-09-12
              • 2017-11-26
              • 2016-06-06
              相关资源
              最近更新 更多