2011-06-03 68 views
8

我想你对以下主题的意见:使用块嘉豪?

假设我们有一个负责实现一个特定目的的方法,但要做到这一点,它需要一个重要的数字支持的本地范围对象,许多其中实施IDisposable

MS编码标准说当使用本地IDisposable对象时,不需要“存活”该方法的范围(不会被返回或不会被分配给某些长期存在的object的状态信息),您应该使用using构造。

的问题是,在某些情况下,你可以得到的using块嵌套“地狱”:

using (var disposableA = new DisposableObjectA()) 
{ 
    using (var disposableB = new DisposableObjectB()) 
    { 
      using (var disposableC = new DisposableObjectC()) 
      { 
       //And so on, you get the idea. 
      } 
    } 
} 

你能以某种方式减轻这一点,如果一些正在使用的对象,从一个共同的基础得到或实施实施IDisposable的共同interface。当然,这需要花费一定的代价,只要你需要真正的对象类型就可以投射这些对象。有时候,这可能是可行的,只要铸造量不失控:

using (var disposableA = new DisposableObjectA()) 
{ 
    using (DisposableBaseObject disposableB = new DisposableObjectB(), 
      disposableC = new DisposableObjectC) 
    { 
      using (var disposableD = new DisposableObjectD()) 
      { 
       //And so on, you get the idea. 
      } 
    } 
} 

另一种选择是不使用using块,直接实现try-catch块。这看起来像:

DisposableObjectA disposableA = null; 
DisposableObjectB disposableB = null; 
DisposableObjectC disposableC = null; 
... 

try 
{ 
    disposableA = new DisposableObjectA(); 
    .... 
} 
finally 
{ 
    if (disposableA != null) 
    { 
      disposableA.Dispose(); 
    } 

    if (disposableB != null) 
    { 
      disposableB.Dispose(); 
    } 

    //and so on 
} 

有趣的是,VS Code Analyzer会将此代码标记为“错误”。它会通知您,并非所有可能的执行路径都确保所有可丢弃对象在超出范围之前将被丢弃。我只能看到发生这种情况时,如果某些物体在丢弃时抛出,而在我看来永远不会发生的情况下发生,如果它发生了,它通常表示某种事情真的搞糟了,而且您可能比您更快,更优雅地退出可以从你的整个应用程序。

所以,问题是:你更喜欢什么样的方法?不管有多少块,或者超过一定的限制,最好是使用嵌套的using块,最好是使用try-catch块?

+0

不确定我会形容嵌套为“地狱”,但一个有效的问题+1 – Jodrell 2011-06-03 12:57:56

回答

16

你不需要花括号,如果有只有一个语句,例如:

using (var disposableA = new DisposableObjectA()) 
using (var disposableB = new DisposableObjectB()) 
using (var disposableC = new DisposableObjectC()) 
{ 
       //And so on, you get the idea. 
} 

这不取决于闲来无事外块发生,虽然。

+0

这就是我在这种情况下所做的。 – Gabe 2011-06-03 12:51:46

+0

我经常使用2或3级的块编写程序,并认为'该死的看起来很丑陋'。感谢你 - 这是DOH之一!时刻。 – 2011-06-03 12:53:13

+0

+1对于多个一次性物品,这是我期望看到它们尽可能安排的方式 – 2011-06-03 12:55:59

7

我想你忘记了using声明(许多人一样),并不一定需要一个代码块,但也可以是单个语句。

using (var disposableA = new DisposableObjectA()) 
using (var disposableB = new DisposableObjectB()) 
using (var disposableC = new DisposableObjectC()) 
{ 
    //And so on, you get the idea. 
} 

我认为这极大地简化了问题:作为你的第一个例子可以写。请注意,如果您需要在调用实现IDisposable的实例之间执行某些操作,则不会有帮助。

我甚至走那么远,有意义窝其它块。 foreach就是一个例子。

IEnumerable<int> ints = ...; 

using (var disposableA = new DisposableObjectA()) 
using (var disposableB = new DisposableObjectB()) 
using (var disposableC = new DisposableObjectC()) 
foreach (int i in ints) 
{ 
    // Work with disposableA, disposableB, disposableC, and i. 
} 

应当指出的是,VS代码分析是正确的,当它告诉你,这是不正确的:

DisposableObjectA disposableA = null; 
DisposableObjectB disposableB = null; 
DisposableObjectC disposableC = null; 
... 

try 
{ 
    disposableA = new DisposableObjectA(); 
    .... 
} 
finally 
{ 
    if (disposableA != null) 
    { 
      disposableA.Dispose(); 
    } 

    if (disposableB != null) 
    { 
      disposableB.Dispose(); 
    } 

    //and so on 
} 

当您使用using堆放在彼此的顶部,它的巢他们在多个try/finally块,像这样:

DisposableObjectA disposableA = null; 
DisposableObjectB disposableB = null; 
DisposableObjectC disposableC = null; 
... 

try 
{ 
    disposableA = new DisposableObjectA(); 

    try 
    { 
     disposableB = new DisposableObjectB(); 

     // Try/catch block with disposableC goes here. 
    } 
    finally 
    { 
     if (disposableB != null) 
     { 
       disposableB.Dispose(); 
     }  
    } 
} 
finally 
{ 
    if (disposableA != null) 
    { 
      disposableA.Dispose(); 
    }  
} 

在你的榜样,如果有异常被抛出时disposableA.Dispose被执行,然后disposableBdisposableC没有得到安置(该finally退出块),如果在disposableB被调用时抛出一个错误,那么disposableC没有关闭,等

+0

感谢您的回答!是的,我知道代码分析器是正确的。就我而言,处置时抛出一个对象是一个非常糟糕的事情的迹象(因为没有真正有效的理由抛出)。在那种情况下,我可能会更好地在受到更多伤害之前尽可能快速和优雅地进行救助。 – InBetween 2011-06-03 13:10:31

+0

@InBetween:虽然没有多大意义,如果你的'Dispose'实现失败了,是的,那很糟糕,但是如果你不处理其他事情就更糟糕了。如果你在堆栈中进一步捕获异常并处理它,该怎么办?现在你拥有的资源应该有他们的'Dispose'方法调用它们没有。 – casperOne 2011-06-03 13:29:11

1

唯一真正的“问题”与你第一个代码示例是深层嵌套,这可以使阅读和维护代码变得困难。作为其他答案的替代方案,建议您只需放下花括号,还可以通过将最深嵌套的代码重构为单独的函数来解决此问题。这将创建和处理一次性物品的顾虑与实际使用它们的问题区分开来。

using (var a = new DisposableObjectA()) 
{ 
    using (var b = new DisposableObjectB()) 
    { 
     using (var c = new DisposableObjectC()) 
     { 
       SomeFunction(a,b,c); 
     } 
    } 
} 
+0

为什么不把这些方法结合起来,如果需要实现'IDisposable'的'N'项,那么你的例子需要N个代码块;对于大'N'来说,这并不能解决可读性问题。 – casperOne 2011-06-03 12:57:38

+0

是的,他们可以合并。我倾向于发现,不久之前,您需要在创建c之前调用b.Init()或其他东西,然后您突然需要回花括号 – 2011-06-03 12:58:32