在C#中使用一个catch-all catch块的finally块是什么用的?

时间:2010-01-11 12:04:54

标签: c# .net exception-handling

考虑以下C#代码结构(S0-S3是任意代码块的占位符):

try
{
    S0;
}
catch (Exception ex)
{
    S1;
}
finally
{
    S2;
}

S3;

如果S1在catch处理程序中抛出异常,finally内的S2仍将执行(但S3不会执行)。

问题

假设S1不能抛出,那么在finally块内部有S2是否有任何意义,而不是在try / catch / finally之外,就在S3之前?

实施例

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    this.cachedException = ex;
}
finally
{
    S2;
}

S3;

finally阻止是否有任何意义?以下代码是否不等同(严格假设catch块内的内容不能抛出):

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    this.cachedException = ex;
}

// No finally block needed (?)
S2;
S3;

次要问题

更新:如果接受上述两个代码块是等效的(根据所述假设),那么考虑到答案中代码清晰度的反馈,它是否可取(并且等效)在finally块内组合S2和S3?

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    this.cachedException = ex;
}
finally
{
    S2; // Put S2 and S3 together inside the `finally` block to guard against
    S3; // future changes in the `catch` filter, or handling code.
}

10 个答案:

答案 0 :(得分:12)

考虑到资源耗尽情况(即内存不足),S1不能抛出的假设是脆弱的。即使有必要(一个很大的if),对代码的微小更改也会引入异常。

由于S2通常关注清理和释放有价值的资源,因此将其置于finally块中可以清楚地传达意图。在可能的情况下,将这些代码放在资源拥有对象的Dispose()方法中,并用using子句替换try / finally子句,可以更好地传达意图(对于C#更具惯用性)。

每当您可以用两种或更多种方式编写内容时,请使用最清晰且最稳定的更改方式。

重新提出问题:如果涉及清理,S3应该放在finally中。如果它预先假定try块的成功,它应该放在finally块之后。如果您的catch语句没有重新抛出,我个人会将其解释为您已成功并可以继续正常操作。然而,整个'保存例外以后重新抛出'的事情让我感到困惑。一般来说,我建议不要在方法之外存储重新抛出的异常。这很不寻常,对我来说似乎很困惑。代码包含的意外越少,维护就越容易(包括你自己,三个月后)。

答案 1 :(得分:10)

你可能没有预料到S1抛出的一种情况:如果线程被中断,异常将自动在catch块的末尾重新抛出。

正如Pontus所说,finally块表示无论发生什么,都应该始终运行此代码。这比捕捉一切然后继续更清楚。

答案 2 :(得分:3)

finally块用于进行资源清理,无论是否发生异常,最后都会调用block,因此它是进行清理工作的理想场所。

答案 3 :(得分:3)

请注意,即使try或catch块包含return语句,也会执行finally子句。

答案 4 :(得分:3)

我认为这类似于在语句内容中添加括号。

虽然你可以争辩说

if (x) y;

不会失败,是什么意思说一些经验不足的程序员以后不会出现并将其编辑为

if (x) y; z;

可能是这样的情况,你的代码不需要finally块,但是如果catch块中的代码发生变化,最好留下它。

if (x) 
{
    y;
}

总是为我赢。

答案 5 :(得分:2)

在您的特定情况下,没有区别......

但是如果你没有捕获Exception,而是Exception的某个特定子类,那么就会有所不同。在查看try-catch-finally块时,这可能是正常的模式。

答案 6 :(得分:1)

您可以确保您的代码今天不会抛出异常,但是当您在6个月内回复它时又如何呢?或者其他人必须做出改变?

使用finally块是一种公认​​的模式,对任何程序员都有意义,如果您的代码执行特殊和不同的操作,绊倒您或其他人。

答案 7 :(得分:1)

在第二种情况下,控件只会在吞下catch块中的任何异常时才会到达S2! Try-Catch不应该在这里使用,而应该小心使用。

例如:

try
{
    // Do something that might throw
}
catch (Exception ex)
{
    // Save the exception to re-throw later
    // NB: This statement cannot throw an exception!
    // this.cachedException = ex;

    // Must swallow any exception here to let control go further!

    // If you're not sure enough [which you and me both are not likely to be] - use finally to execute S2 in any condition
}

// No finally block needed (?)
S2;
S3;

答案 8 :(得分:1)

为了保证在发生异常时清理资源,请使用try / finally块。关闭finally子句中的资源。使用try / finally块可确保即使发生异常也会处置资源....有关详细信息,请检查http://itpian.com/Coding/5143-Need-for-Finally-block.aspx

答案 9 :(得分:0)

不,你不必使用它。在这种情况下不需要它。它是您的选择,取决于您可能/可能不会使用的资源。如果您需要清理可能拥有的任何资源,那么finally块是最佳选择。