【问题标题】:Where should I place try catch?我应该把try catch放在哪里?
【发布时间】:2015-02-08 10:44:30
【问题描述】:

学习记录错误。 这是贯穿我整个项目的基本代码结构。

我被告知,try 块只能放在事件处理程序中。 但是在记录错误时,需要知道是哪个方法导致了错误。 所以,在这种情况下,我是否也应该在AllIsFine() 和SaveData() 中保留try 块。 如果是,那么它应该记录错误还是只记录throw。

最佳/标准做法是什么。

DataContext objDataContext = new DataContext();
protected void btn_Click(object sender, EventArgs e)
{
  try
   {
     if(AllIsFine())
      {
        objDataContext.SaveData();
      }
   }
  catch(Exception ex)
   {
     //some handling
   }
}

private bool AllIsFine()
{
   //some code
}

编辑:当然,我们会尝试确保它永远不会引发异常,但不实用。我是这样看的。部署后,我唯一的访问权限是日志,需要获取尽可能多的信息。所以在这种情况下,(以及这种结构),你建议在哪里保留 try catch

【问题讨论】:

  • 您也应该记录未处理的异常。例如,参见here。重新抛出和处理(以及非常重要的finally 概念)实际上与日志记录本身无关,但您当然要学习它。鉴于有关事件处理程序的建议在上下文中是有效的,不会有 try above,因此您应该自己处理可能的异常,否则会出现未处理的异常(这将传播并导致您的应用程序崩溃) .

标签: c# .net exception-handling try-catch


【解决方案1】:

我被告知,try 块只能放在 Event 中 处理程序

这不是真的。
有不同类型的异常,一些你需要处理,一些你应该抛出,一些你应该捕获,一些你不应该。请阅读this。

总结:

  • 您应该捕获可以对它们执行某些操作的异常
  • 如果您只想记录异常,请捕获、记录,然后使用 throw 而不是 throw ex(它会重置调用堆栈)
  • 如果可以,请防止异常发生,检查防止异常的条件(IndexOutOfRangeException、NullPointerException、ArgumentNullException)这样做而不是执行操作并捕获异常
  • 不要捕获致命异常;无论如何,您对它们无能为力,并且试图使情况变得更糟。
  • 修复您的代码,使其永远不会触发愚蠢的异常 - “索引超出范围”异常不应在生产代码中发生。
  • 通过调用那些抛出非异常的烦人方法的“尝试”版本,尽可能避免烦人的异常 情况。如果你无法避免调用一个令人头疼的方法,抓住它 令人烦恼的例外情况。
  • 始终处理表示意外外部条件的异常;一般来说,预期是不值得或不实际的 每一个可能的失败。只需尝试操作并准备好 处理异常。

【讨论】:

  • 这可能有点复杂,因为您现在正在学习日志记录,但顺便提一下,日志记录被视为一个跨领域的关注点。如果您有共同的抽象(这是整个项目中代码的基本结构),您可以使用 decorator pattern 将 try-catch 放入装饰器中,您将在任何情况下使用该装饰器抽象——这将减少与逻辑无关的代码量(因为它不处理异常,你只需记录它们)
  • 面向切面编程是CCC的解决方案,你可以使用postsharp在C#中使用AOP
  • @dotctor。此时,我要做的就是记录所有异常(除了我非常确定的异常)。这是否意味着,我需要尝试在所有方法/处理程序中。
  • 这取决于您的程序控制流程,您可以将程序的入口点(通常是 Main 方法)包装在 try/catch 中,如果发生异常,则记录它并终止程序.您应该阅读我提到的文章并了解异常类型之间的区别。如果您只想调用一个方法并在发生错误时记录日志,那么您可以将调用包装为 self 或将方法的整个主体包装在 try/catch 中。
  • 很高兴看到您引用 Eric Lippert 关于捕获异常的文章。该文章的链接会更好。
【解决方案2】:

但是在记录错误时,需要知道,哪个方法 导致错误。

您应该为此使用调用堆栈。

尽量防止异常,而不是捕捉它们,如果你想捕捉错误,试着捕捉一个特定的错误,我的意思是,不是试图捕捉异常,而是像 InvalidCastException 这样的特定的东西。

捕获错误后,您应该记录异常,但不要抛出异常来记录它。

【讨论】:

    【解决方案3】:

    try-catch 语句由一个 try 块和一个或多个 catch 子句组成,这些子句为不同的异常指定处理程序。

    object o2 = null;
    try
    {
        int i2 = (int)o2;   // Error
    }
    

    可以在同一个 try-catch 语句中使用多个特定的 catch 子句。可以在 catch 块中使用 throw 语句来重新抛出 catch 语句捕获的异常。您可以捕获一个异常并抛出另一个异常。执行此操作时,将捕获的异常指定为内部异常

    catch (FileNotFoundException ex)
    {
        // FileNotFoundExceptions are handled here.
    }
    catch (InvalidCastException ex) 
    {
        // Perform some action here, and then throw a new exception.
        throw new YourCustomException("Put your error message here.", ex);
    }
    

    throw ex; 基本上就像从那个点抛出异常,所以堆栈跟踪只会转到您发出 throw ex; 语句的地方

    【讨论】:

      【解决方案4】:

      我想了一会儿你的问题,即使已经回答了,我也想分享一下我的想法。

      一般

      在谈论异常处理时,您必须牢记什么是异常: 当代码无法按预期运行时会发生异常,从而导致应用程序的行为/状态不一致。 这个想法将我们引向一条重要的规则:不要不要捕获你无法处理的异常!异常系统提供安全性。考虑一个连接到数据库并且连接引发异常的会计软件。如果您只记录异常并且不向用户提供任何有关此的通知,他将开始使用该软件,并且当他尝试保存时,数据将丢失!

      你看,处理异常的在哪里很重要:

      • 其中一些将由用户处理(通常在 UI 层中,例如 MVVM/MVC 中的视图),例如示例中的数据库异常 - 只有用户可以决定是否需要数据库,连接字符串已更改或者重试是否可以解决问题。
      • 其他的可以在发生的地方处理;例如,与某种硬件交互的代码通常会实现具有某种重试或断路器逻辑的方法。

      架构

      where 决策也受到架构的影响: 让我们回到数据库异常场景:您无法在 UI 层中处理特定异常,例如 MySQL 特定异常 (MySqlException),因为这会导致从视图层到特定模型层实现的边界,并且分层架构将是破碎的。但是,这不是主题,因为您的问题是关于记录异常,但是您应该记住这一点,并且可能会这样考虑:捕获具体库的异常或属于它们发生的特定层/依赖项的异常并记录他们在那里和throw new,更通用的(可能是自己创建的)异常。这些异常可以在视图层处理并呈现给用户。

      实现

      在哪里的问题也很重要。由于日志记录被视为一个交叉问题。您可以考虑一个 AOP 框架,或者正如我在评论中建议的那样,使用装饰器模式。您可以通过控制使用的基本反转来组成日志装饰器,并且通过将所有内容包装在 try-catch 块中,您的代码将更加简洁。 如果您在应用程序中使用(或计划使用)DI 容器,您还可以使用拦截,这将允许您通过指定的行为(如try-catch)动态包装您的方法调用。

      总结

      处理您的异常,您可以在其中处理它们并记录它们,您可以从中获取最相关的数据(例如上下文)以重现错误。但请记住,异常是一种安全机制——当你的应用程序不再处于安全状态时,它将停止你的应用程序——当你不转发你的异常(通过使用throw)并且只记录它们时,这可能会导致不是最好的用户体验。

      如果您需要任何代码示例,只需索取即可;)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-30
        • 2016-08-25
        • 2013-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-06
        相关资源
        最近更新 更多