try-catch-finally的用例同时包含catch和finally

时间:2010-02-17 10:15:21

标签: c# java vb.net language-agnostic try-catch-finally

我理解 try-catch 的工作方式以及 try-finally 的工作原理,但我发现自己在两种完全不同的场景中使用(通常):

  • try-finally (或C#和VB中的using)主要用于某些中型代码块,它使用一些需要正确处理的资源。
  • 主要使用
  • try-catch
    • 围绕单个语句,可以以非常具体的方式失败或
    • (作为一个包罗万象)在应用程序的一个非常高的级别,通常直接在一些用户界面操作下面。

根据我的经验, try-catch-finally 适合的情况,即我想要捕获某个特定异常的块完全我使用一些可支配资源的同一块非常罕见。然而,C#VBJava的语言设计者似乎认为这是一种非常常见的情况; VB设计师even think about adding catch to using

我错过了什么吗?或者我对限制性地使用 try-catch 过于迂腐?


编辑:澄清:我的代码通常看起来像这样(为了清晰起见,函数展开):

Try
    do something
    Aquire Resource (e.g. get DB connection)
    Try 
        do something
        Try
            do something that can fail
        Catch SomeException
            handle expected error
        do something else... 
    Finally 
        Close Resource (e.g. close DB connection)
    do something
Catch all
    handle unexpected errors

这似乎比将两个捕获中的任何一个放在与最终相同的级别上更有意义,只是为了避免缩进。

10 个答案:

答案 0 :(得分:16)

来自MSDN

的引用
  

catch的常见用法,最后   一起是获取和使用   处理try块中的资源   捕捞中的特殊情况   阻止,并释放资源   最后一块。

所以为了让它更清晰,想想你要运行的代码,在99%的情况下它运行得非常好但是在块中的某个地方可能会出现错误,你不知道在哪里和创造的资源很昂贵。

为了100%确定资源被处理掉,你使用finally块,但是,你想要指出发生错误的1%的情况,因此你可能想要设置登录捕捉部分。

这是一种非常常见的情况。

编辑 - 实际示例

这里有一些很好的例子:SQL Transactions with SqlTransaction Class。这只是使用Try,Catch和amp;的众多方法之一。最后,它很好地证明了这一点,即使using(var x = new SqlTranscation)有时可能有效。

所以这里。

var connection = new SqlConnection();

var command = new SqlCommand();

var transaction = connection.BeginTransaction();

command.Connection = connection;
command.Transaction = transaction;

这里有更有趣的部分

///
/// Try to drop a database
///
try
{
    connection.Open();

    command.CommandText = "drop database Nothwind";

    command.ExecuteNonQuery();
}

因此,让我们假设上述因某种原因而失败并抛出异常

///
/// Due to insufficient priviligies we couldn't do it, an exception was thrown
///
catch(Exception ex)
{
    transaction.Rollback();
}

交易将被回滚!请记住,您对try / catch中的对象所做的更改将不会回滚,即使您将其嵌入使用中也不会回滚!

///
/// Clean up the resources
///
finally
{

    connection.Close();
    transaction = null;
    command = null;
    connection = null;
}

现在资源已经清理好了!

答案 1 :(得分:12)

不是你问题的答案,而是一个有趣的事实。

C#编译器的Microsoft实现实际上无法处理try-catch-finally。当我们解析代码时

try { Foo() } 
catch(Exception e) { Bar(e); }
finally { Blah(); }

我们实际上假装代码是写的

try 
{
   try { Foo(); }
   catch(Exception e) { Bar(e); }
}
finally { Blah(); }

这样编译器的其余部分 - 语义分析器,可达性检查器,代码生成器等 - 只需要处理try-catch和try-finally,永远不要尝试catch-finally。在我看来,这是一个愚蠢的早期转变,但它确实有效。

答案 2 :(得分:10)

示例:

Try
   Get DB connection from pool
   Insert value in table
   do something else...
Catch
   I have an error... e.g.: table already contained the row I was adding
   or maybe I couldn't get the Connection at all???
Finally
   if DB connection is not null, close it.

你无法真正得到一个更“常见”的例子。遗憾的是,有些人仍然忘记将最后的连接放在最后,它所属的地方,而不是在Try块... :(

答案 3 :(得分:6)

经常编写如下代码:

Handle h = ...
try {
   // lots of statements that use handle
} catch (SomeException ex) {
   // report exception, wrap it in another one, etc
} catch (SomeOtherException ex) {
   // ...
} finally {
   h.close();
}

所以也许你只是过于迂腐...例如通过对各个语句进行try / catch。 (有时候这是必要的,但根据我的经验,你通常不需要如此精细。)

答案 4 :(得分:3)

嵌套try / catch / finally块没有任何问题。我经常使用这个。即当我使用一些需要处理或关闭的资源时,我只想在一个较大的代码单元周围只有一个catch块,如果它内部发生了一些错误则需要中止。

try {
    // some code
    SomeResource res = new SomeResource();
    try {
        res.use();
    } finally {
        res.close();
    }
    // some more code
} catch( Exception ex ) {
    handleError( ex );
}

在任何一种情况下(错误与否)都会尽早关闭资源,但仍会处理在单个catch块中创建或使用资源的所有可能错误。

答案 5 :(得分:3)

我认为你是对的。来自。Net Framework Design Guidelines,由微软的一些顶级架构师撰写:

  

不要重叠。例外应该   经常被允许传播   调用堆栈。

     

在编写良好的代码中,try-finally [或   使用]比通常更常见   试着抓。看起来似乎   起初违反直觉,但要抓住   块中不需要令人惊讶   案件数量。另一方面,   你应该总是考虑是否   try-finally [或使用]可能有用   清理。

第230页第7.2节

答案 6 :(得分:1)

如果您需要在所有情况下处理某些内容并使用案例记录错误和/或通知用户,我几乎总是会使用try-catch-finally。

答案 7 :(得分:1)

如下:

  Dim mainException as Exception = Nothing

  Try
    ... Start a transaction
    ... confirm the transaction
  Catch Ex as Exception
    mainException = Ex
    Throw
  Finally
    Try
      ... Cleanup, rolling back the transaction if not confirmed
    Catch Ex as Exception
      Throw New RollbackFailureException(mainException, Ex)
    End Try
  End Try

假设RollbackFailureException包含“OriginalException”字段以及“InnerException”,并接受两者的参数。人们不希望隐藏在回滚期间发生异常的事实,但是也不想丢失导致回滚的原始异常(并且可能提供关于回滚发生的原因的一些线索)。

答案 8 :(得分:0)

另一种用途是在使用System.Web.Mail邮件对象发送电子邮件时将文件句柄配置到电子邮件附件。当我不得不以编程方式打开Crystal Report,将其保存到磁盘,将其附加到电子邮件,然后将其从磁盘中删除时,我发现了这一点。在Finally中需要显式.Dispose()以确保我可以删除它,特别是在抛出异常的情况下。

答案 9 :(得分:-1)

  

根据我的经验,try-catch-finally最合适的情况,即我想要捕获某个特定异常的块与我使用某些可处理资源的块完全相同,这种情况极为罕见。然而,C#,VB和Java的语言设计者似乎认为这是一个非常常见的场景; VB设计师甚至考虑将catch添加到使用中。

在:

try {
   //use resource
} catch (FirstException e) {
   // dispose resource
   // log first exception
} catch (SecondException e) {
   // dispose resource
   // log first exception
} catch (ThirdException e) {
   // dispose resource
   // log first exception
}

我:

try {
  //use resource
} catch (FirstException e) {
  // log first exception
} catch (SecondException e) {
  // log first exception
} catch (ThirdException e) {
  // log first exception
} finally {
  // dispose resource
}
感觉干扰?)