使用大量块?

发布于 2024-11-13 08:48:36 字数 1792 浏览 2 评论 0原文

我想了解您对以下主题的看法:

想象一下,我们有一种方法负责实现一个特定目的,但要做到这一点,它需要大量本地范围对象的支持,其中许多对象实现 IDisposable

MS 编码标准规定,当使用不需要在方法范围内“生存”的本地 IDisposable 对象时(不会返回或不会分配给某些寿命较长的 对象的状态信息) 例如)您应该使用 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的code>接口。这当然是以每当您需要对象的真实类型时就必须强制转换所述对象为代价的。有时,只要转换量不失控,这就是可行的:

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 分析器会将此代码标记为“错误”。它会告诉您并非所有可能的执行路径都确保所有一次性对象在超出范围之前都将被处置。我只能看到这种情况发生,如果某个对象在处理时抛出,在我看来,这永远不应该发生,如果确实发生,通常表明发生了真正混乱的事情,你可能最好像你一样快速而优雅地退出可以从您的整个应用程序。

那么,问题是:您更喜欢哪种方法?无论有多少个嵌套 using 块总是更可取,还是超过一定限制后,最好使用 try-catch 块?

I'd like your opinion on the following subject:

Imagine we have a method that is responsible of achieving one specific purpose, but to do so, it needs the support of an important number of locally scoped objects, many of them implementing IDisposable.

MS coding standards say that when using local IDisposable objects that need not "survive" the scope of the method (will not be returned or will not be assigned to the state information of some longer lived object for instance) you should use the using construct.

The problem is, that in some situations you can get a nested "hell" of using blocks:

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

You can somehow mitigate this if some of the objects you are using derive from a common base or implement a common interface that implements IDisposable. This of course comes at a cost of having to cast said objects whenever you need the true type of the object. Sometimes this can be viable as long as the amount of casting doesn't get out of hand:

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.
          }
     }
}

The other option is not to use using blocks and implement directly try-catch blocks. This would look like:

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
}

Funny thing, is that VS Code Analyzer will flag this code as "wrong". It will inform you that not all possible execution paths ensure that all disposable objects will be disposed before going out of scope. I can only see that happening if some object throws while disposing which in my opinion should never happen, and if it does, its normally a sign that something really messed up is going on and you are probably better of exiting as fast and gracefully as you can from your entire app.

So, the question is: what approach do you like better? Is it always preferable to use nested using blocks no matter how many, or, past a certain limit, its better to use try-catch block?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柠栀 2024-11-20 08:48:36

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

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

但这确实取决于外部块中没有发生任何其他事情。

You don't need the curly brackets if there's just one statement, for example:

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

This does depend on nothing else happening in the outer blocks though.

所有深爱都是秘密 2024-11-20 08:48:36

我认为您忘记了 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 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
}

当您使用彼此堆叠的 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< /code> 未关闭等

I think you are forgetting that the using statement (like many others), don't necessarily require a code block, but can be a single statement. Your first example can be written as:

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

I think this eases the problem tremendously. Note, it doesn't help if you need to do something between the invocation of the instances that implement IDisposable.

I even go so far as to nest other blocks that make sense. foreach is an example.

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.
}

It should be noted that VS Code analyzer is correct when it tells you that this is incorrect:

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
}

When you use using stacked on top of each other, it nests them in multiple try/finally blocks, like so:

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();
     }    
}

In your example, if there is an exception that is thrown when disposableA.Dispose is executed, then disposableB and disposableC do not get disposed (the finally block is exited), if an error is thrown when disposableB is called, then disposableC is not closed, etc.

很酷不放纵 2024-11-20 08:48:36

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

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

The only real 'problem' with your first code sample is the deep nesting, which can make reading and maintaining code hard. As an alternative to the other answers suggesting you simply drop the curly braces, you can also work round this by refactoring the most deeply nested code out into a separate function. This separates the concerns of creating and disposing your disposable objects from actually using them.

using (var a = new DisposableObjectA())
{
    using (var b = new DisposableObjectB())
    {
         using (var c = new DisposableObjectC())
         {
              SomeFunction(a,b,c);
         }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文