使用块丰富?

InB*_*een 8 c# code-analysis code-design

我希望您就以下主题发表意见:

想象一下,我们有一个方法负责实现一个特定的目的,但要做到这一点,它需要大量本地范围的对象的支持,其中许多实现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.
          }
     }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用的某些对象派生自公共库或实现实现的公共interface,则可以以某种方式缓解此问题IDisposable.当然,这需要在需要对象的真实类型时必须投射所述对象.有时,只要铸件的数量不会失控,这是可行的:

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.
          }
     }
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是不使用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
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,VS Code Analyzer会将此代码标记为"错误".它将通知您,并非所有可能的执行路径都确保在超出范围之前处置所有一次性对象.我只能看到发生的事情,如果某些物体在处置时会抛出我认为永远不会发生的事情,如果确实如此,它通常表明事情真的搞砸了,你可能更好地退出,就像你一样快速和优雅可以从你的整个应用程序.

所以,问题是:你更喜欢什么方法?是否总是更喜欢使用嵌套using块,无论多少,或者,超过某个限制,使用try-catch块更好?

Jon*_*ton 16

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

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

这确实取决于外部块中没有其他事情发生.


cas*_*One 7

我认为你忘记了using语句(像许多其他人一样),不一定需要代码块,但可以是单个语句.你的第一个例子可以写成:

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

我认为这极大地缓解了这个问题.注意,如果您需要在调用实现的实例之间执行某些操作,则无效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.
}
Run Code Online (Sandbox Code Playgroud)

应该注意的是,当VS Code分析器告诉您这是不正确时,它是正确的:

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
}
Run Code Online (Sandbox Code Playgroud)

当你使用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();
     }    
}
Run Code Online (Sandbox Code Playgroud)

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