2009-02-21 26 views
2

可以说你有一些资源清理如下:这是C#。finally块中的多项式操作

try{/*stuff*/} 
catch(Exception e) {/*rollback logs etc.*/} 
finally{ 
if(context.Transaction != null) 
    context.Transaction.Dispose(); 
context.Connection.Close(); 
context.Connection.Dispose(); 
} 

这样做会更健壮吗?

try{/*stuff*/} 
catch(Exception e) {/*rollback logs etc.*/} 
finally{ 
try{ 
    if(context.Transaction != null) 
    context.Transaction.Dispose(); 
}catch(Exception e){/*logs*/} 
finally{ 
    context.Connection.Close(); 
    context.Connection.Dispose(); 
} 
} 

这样,如果transaction.dispose管理失败,连接将被给予关闭的机会。

+0

通过这种逻辑,你可以将嵌套try-catch结构的概念应用到无限级别。它仍然听起来像健壮? – Cerebrus 2009-02-21 15:40:46

+0

@Cerberus:只有你有无限的资源,才会遇到这种情况? – 2009-02-21 16:30:58

回答

12

这样做会更健壮吗?

你会更好地使用多个块。

首先,你的catch块会吃掉所有的异常并且不需要(可以尝试......最后没有任何捕获)。只有在可以处理(或增加值)异常的情况下才使用catch。

不过还好:

using (var resA = GetMeAResourceNeedingCleanUp()) 
using (var resB = new AnotherResourceNeedingCleanUpn(...)) { 
    // Code that might throw goes in here. 
} 

NB。一旦例外情况回复,最后块被清除,抛出其他异常可能导致(至多)混淆正在处理哪个。第二条准则:

不要从Dispose方法或终结器中抛出异常。如果您需要允许用户处理清理失败,请提供一个可以报告其失败的单独Close方法。

注意,该 “框架设计指南”(2 第二 ED)具有此作为(§9.4.1):

AVOID除下临界从投掷内的Dispose(布尔)一个异常 包含进程已被破坏的情况(泄漏,不一致的 共享状态等)。

+0

我相信你的使用语句是错误的,因为你通常需要指定连接来创建事务 - 而且通常应该首先清理事务。 – 2009-02-21 15:53:16

0

为什么Dispose调用会失败?某些时候你也可能太小心了。像包裹每一个“新”的语句在try/catch的情况下,内存用完......

3

三点:

  • 你不需要调用Close,以及处置
  • 处置在单独的finally块中的交易是一个更好的主意,因为它可以防止处置期间抛出的异常。 (它不应该经常发生,但它可能会发生。)
  • using声明几乎总是处理资源的最干净的方式。我用这个就算我也想要一个try/catch块,只是因为它说:“这使用,我想在该块结束时被布置在资源”

结合的惯用方式这将导致两个using语句:

using (SqlConnection conn = ...) 
{ 
    using (Transaction trans = ...) 
    { 
    } 
} 

如果你想避免过度缩进,你可以这样写:

using (SqlConnection conn = ...) 
using (Transaction trans = ...) 
{ 
} 
0

我不喜欢我的最后条款过于verbo (或者任何条款)。我会重构你的资源清理到一些实用程序类。保留所有嵌套的try和“if null”条件,以便更好地重用。例如,因为您的清理逻辑仅驻留在一个位置,所以稍后可以轻松改变您的想法,确定是否真的需要调用Dispose()。

更重要的是,您的应用程序代码变得更加易于理解。

try{/*stuff*/} 
catch(Exception e) {/*rollback logs etc.*/} 
finally{ 
    Utility.cleanup(context); 
}