在哪里实现try catch块?

时间:2010-09-02 08:11:46

标签: c# error-handling

我总是在努力放置try catch块。例如,我有一个数据库类,其方法接受两个参数。 FindObject(字符串where,string order)。此方法使用指定的where和order字符串执行sql查询。

在一个类中我有一个名为IsUsed的属性,这个属性如下所示:

public bool IsUsed 
{  
 get
 {   
  ClassA a = new ClassA();   
  Collection<ClassA> myCollection = a.FindObject("Id = 1","");

  if(..) // etc  
 } 
}

这种方法是否聪明并不重要,如果执行sql查询出错,我只想知道try catch的放置位置。

我应该在哪里放置try catch,以便我可以通知用户出错了什么?

  1. 在FindObject方法中?
  2. 在IsUsed属性中?
  3. 我在哪里调用IsUsed属性?
  4. 其他地方?但是哪里

12 个答案:

答案 0 :(得分:11)

我尝试遵循一个相当简单的规则:如果我能以合理的方式处理异常,我会设置一个try..catch块 。如果我对异常没有任何意义,我会让它冒泡到调用代码。

作为旁注,我会避免在属性getter中执行(可能很长的)数据库调用。我通常尝试设置刚刚设置的属性或从支持字段获取值,让其他方法执行数据库查找等。这将使编写调用代码的人更容易预测代码(属性访问通常是一种廉价的操作,而方法调用通常更昂贵)。

答案 1 :(得分:6)

如果你能对它做点什么,那就抓住异常。否则,在应用程序的“最高”级别捕获异常,并执行处理它的任何操作,包括终止您的应用程序。

在具有UI的应用程序中,“最高”级别通常是事件处理程序,如搜索按钮的单击处理程序。对于后台服务,“最高”级别通常是线程触发或计时器回调。

  UI application                          Background service

| System Code    |                      | System Code    |
+----------------+                      +----------------+
| Event Handler  | <- try/catch here -> | Thread proc    |
|                |                      |                |
| ...            |                      | ...            |
|                |                      |                |
| Method         | <- throw here     -> | Method         |

如果您将异常传播回系统代码,则会出现未处理的异常,并且您的应用程序将崩溃。

在UI应用程序中处理异常通常涉及显示消息框。一些例外不是致命的,并且可以重试操作(例如,如果文件丢失或数据库查询失败),但其他例外是​​致命的,唯一的选择是终止应用程序。

后台服务将记录异常并可能重试该操作。如果多次重试失败,则日志记录级别可能会增加,以引起操作员的注意。

异常处理方面的良好做法:

  • 如果您捕获异常并重新抛出自己的异常,则将原始异常包装为新异常的InnerException
  • 如果您捕获异常或许要进行一些清理但重新抛出它,因为您希望它冒泡,那么请始终使用throw重新抛出它而不指定任何异常。这将确保不破坏原始堆栈跟踪。
  • 在大多数情况下,您应该只在顶级处理程序中捕获基类Exception
  • 使用finally块或更好的IDisposable模式在代码中执行正确的清理。
  • 将异常视为开发人员的“用户界面”,并相应地格式化异常消息。如果您需要为技术含量较低的用户提供更加精美的用户界面,您应该隐藏更多技术性的内容。
  • 尝试仅针对意外错误等异常情况使用例外。不要为常见错误情况抛出异常。检查字典中是否存在键不应该抛出异常,而是返回true / false值。

要回答您的具体问题,我认为您不应该在您的财产中发现任何例外情况。

答案 2 :(得分:3)

你应该把try-cacth块放在你可以对捕获的异常做一些有意义的事情的地方。就像你记录它或显示给用户一样。不要仅仅为了它而捕获异常。

答案 3 :(得分:2)

那么它应该是需要try..catch块的地方,以及它可以处理的地方。我假设这将在FindObject方法中。

因此,在FindObject方法中,例如,捕获并处理 SqlException。如果需要将其移动到更高级别以便更好地处理,那么抛出异常,或者让它简单地冒泡。

答案 4 :(得分:2)

我能想到的唯一规则就是“在最低级别,您可以实际使用这些信息做一些有用的事情而且不会复制异常处理代码”。

例如,如果您通常希望以相同的方式捕获所有与数据库访问相关的异常,我将创建一个数据抽象层(有很多很好的理由这样做),并将try-catch块放在那里。

另一个极端是一个网络应用程序,你不期望任何这样的例外,Elmah会抓住例外。在这种情况下,你根本不想使用try-catch块,因为它会搞砸日志记录。

答案 5 :(得分:2)

这很容易回答:你可以适当地处理它。

是的,在给定的地方,你能否根据渔获量做出有用的决定?你能回复别的吗?你能告诉别人什么吗?您是否可以将错误转换为应用程序的其他某个层的更有用的错误?

如果答案是肯定的,那么你就抓住它了。如果对所有这些都不是,那就不要抓住它,让另一个区域这样做。

FWIW,恕我直言,您的Get实施也过于复杂。我认为通常情况下,人们不会指望房产会做那种“工作”。不过我的看法。

答案 6 :(得分:1)

通常,作为rule of thumb

  

try块包含可能导致异常的保护代码

在你的情况下,你可以像下面那样处理它:

FindObject()
{
try{}catch{throw;//or throw new Exception("Some info");
}

IsUser
{
try{...a.FindObject("Id = 1",""); return true;}
catch(Exception ex){Log(ex); return false;}
}

<强> - 编辑 -

这是对@controlbreak评论的反应:

MSDN说:

  

您可以使用throw语句显式抛出异常。您还可以使用throw语句再次抛出捕获的异常。 良好的编码习惯可以将信息添加到重新抛出的异常中,以便在调试时提供更多信息。

答案 7 :(得分:1)

这取决于您要处理的位置以继续处理。假设调用代码负责通知用户,那么一个好的地方就是调用代码。调用代码可能需要在用户通知用户后询问用户其他内容。

还要考虑重构引发的异常。如果IsUsed驻留在与数据库无关的类中,FindObject可以抛出SqlServerTimedOutException,它将弹出整个调用堆栈。如果这种原理完全令人困惑,请抓住异常并重新抛出它:

     public bool IsUsed  
     {   
         get 
         {    
          ClassA a = new ClassA();    
          Collection<ClassA> myCollection;

          try
          {
              myCollection = a.FindObject("Id = 1","");
          }
          catch (SqlServerTimedOutException ex)
          {
              throw new MyApplicationException("Cannot evaluate if is in use.", ex);
          }

          if(..) // etc   
         } 
    }

但是不要过度使用它,因为它会使代码变得相当丑陋。仔细考虑。

答案 8 :(得分:0)

就我而言,我把尝试抓住块:

  • 当需要最终来清理资源时
  • 当发生故障时需要特定的操作流程(用户重试,记录......)

通常,我允许异常冒泡到顶级,通常是一些winform控件事件处理程序。我在这里使用应用程序级别的异常处理方法来放置 try catch

否则,当我特别懒,我将 Application.ThreadException 事件挂钩到 static void Main() MessageBox.Show >切入点方法。

答案 9 :(得分:0)

尽早捕获错误,并设计API调用,以便它可以在不使用异常的情况下处理failour。未捕获的异常是稳定性的威胁,如果在边缘情况下抛出异常,您可能不知道五层,除非较低的API表明它们可能会失败。

通常你可以设计api让结果对象保持执行状态,这会告诉消费者这个方法可能会失败以及它是如何失败的。

这种方法通常会为您提供有意义的方法来处理较低api部分中的异常,而不是仅仅让它们向上调用调用代码。

这就是说有些语言你可以声明一个方法会抛出异常以及它抛出的异常,但你编写的C#没有这个功能。

答案 10 :(得分:-1)

请参阅,如果您收到任何错误,请将IsUsed设置为默认值。像这样:

public bool IsUsed 
{  
 get
 {   
   try{
     ClassA a = new ClassA();   
     Collection<ClassA> myCollection = a.FindObject("Id = 1","");

     if(..) // etc  
   } 
   catch { return false;}
 } 
}

因此,只要FindObject中出现任何错误,您就会产生错误。

答案 11 :(得分:-1)

我不同意你们大多数人的意见。

首先,我想推荐Joel的这篇精彩文章:http://www.joelonsoftware.com/items/2003/10/13.html

记住这一点,我只使用try-catch块尽可能接近它们可能出错的地方 UI显然希望听到它。让它有一个错误值,但不要隐藏实际问题的原因(通过捕获其他意外错误)。

如果我正确理解你的代码,我想我会在实际查询中使用try-catch,并使IsUsed nullabe确保我理解它没有赋值。