嵌套&QUOT处理;采用"在C#中的语句 [英] Dealing with nested "using" statements in C#

查看:149
本文介绍了嵌套&QUOT处理;采用"在C#中的语句的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到,嵌套的使用语句的水平在我的code近来增加。究其原因,可能是因为我用异步/的await 模式,这往往增加了越来越多至少多了一个使用 CancellationTokenSource CancellationTokenRegistration

I've noticed that the level of nested using statements has lately increased in my code. The reason is probably because I use more and more of async/await pattern, which often adds at least one more using for CancellationTokenSource or CancellationTokenRegistration.

那么,如何降低使用 嵌套,所以code看起来并不像圣诞树?类似的问题已经被问过这之前,我想总结一下我从答案的教训。

So, how to reduce the nesting of using, so the code doesn't look like Christmas tree? Similar questions have been asked on SO before, and I'd like to sum up what I've learnt from the answers.

using (var a = new FileStream())
using (var b = new MemoryStream())
using (var c = new CancellationTokenSource())
{
    // ... 
}

这可能工作,但往往有一些$ C $ 之间用c (例如,它可能还为时过早创建另一个对象):

This may work, but often there's some code between using (e.g. it may be too early to create another object):

// ... 
using (var a = new FileStream())
{
    // ... 
    using (var b = new MemoryStream())
    {
        // ... 
        using (var c = new CancellationTokenSource())
        {
            // ... 
        }
    }
}

联合同类型的对象(或转换为的IDisposable )转换成单使用,例如:

Combine objects of the same type (or cast to IDisposable) into single using, e.g.:

// ... 
FileStream a = null;
MemoryStream b = null;
CancellationTokenSource c = null;
// ...
using (IDisposable a1 = (a = new FileStream()), 
    b1 = (b = new MemoryStream()), 
    c1 = (c = new CancellationTokenSource()))
{
    // ... 
}

这有相同的限制,以上,再加上是比较罗嗦,少可读性,海事组织。

This has the same limitation as above, plus is more wordy and less readable, IMO.

这是一个的 preferred方式的,据我了解。 不过,我很好奇,为什么会下被认为是不好的做法?

This is a preferred way, as far as I understand. Yet, I'm curious, why would the following be considered a bad practice?

public class DisposableList : List<IDisposable>, IDisposable
{
    public void Dispose()
    {
        base.ForEach((a) => a.Dispose());
        base.Clear();
    }
}

// ...

using (var disposables = new DisposableList())
{
    var a = new FileStream();
    disposables.Add(a);
    // ...
    var b = new MemoryStream();
    disposables.Add(b);
    // ...
    var c = new CancellationTokenSource();
    disposables.Add(c);
    // ... 
}

[更新] 里有评论相当多的有效点的嵌套使用语句确保的Dispose 将调用每个对象上,即使一些内部的Dispose 调用抛出。然而,有一个有点模糊的问题:所有的嵌套异常可能是由废弃的嵌套的'使用'帧都将被丢失,除了最外层抛出。更多关于此这里

[UPDATE] There are quite a few valid points in the comments that nesting using statements makes sure Dispose will get called on each object, even if some inner Dispose calls throw. However, there is a somewhat obscure issue: all nested exceptions possibly thrown by disposing of nested 'using' frames will be lost, besides the most outer one. More on this here.

推荐答案

在一个单一的方法,第一种选择将是我的选择。然而,在某些情况下, DisposableList 是非常有用的。特别是,如果你有很多一次性的领域,所有需要处理的(在这种情况下,你不能使用使用)。给出的实施是良好的开端,但它有一些问题(由阿列克谢在评论中指出):

In a single method, the first option would be my choice. However in some circumstances the DisposableList is useful. Particularly, if you have many disposable fields that all need to be disposed of (in which case you cannot use using). The implementation given is good start but it has a few problems (pointed out in comments by Alexei):


  1. 需要你记住的项目添加到列表中。 (虽然你也可以说你要记住使用 使用。)

  2. 中止处置过程中如果的Dispose方法之一抛出,留下其余的项目未处置。

让我们来解决这些问题:

Let's fix those problems:

public class DisposableList : List<IDisposable>, IDisposable
{
    public void Dispose()
    {
        if (this.Count > 0)
        {
            List<Exception> exceptions = new List<Exception>();

            foreach(var disposable in this)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception e)
                {
                    exceptions.Add(e);
                }
            }
            base.Clear();

            if (exceptions.Count > 0)
                throw new AggregateException(exceptions);
        }
    }

    public T Add<T>(Func<T> factory) where T : IDisposable
    {
        var item = factory();
        base.Add(item);
        return item;
    }
}

现在我们赶上从的Dispose 调用任何异常,并抛出一个新的 AggregateException 后通过的所有项目将。我添加了一个帮手添加方法,使一个简单的用法:

Now we catch any exceptions from the Dispose calls and will throw a new AggregateException after going through all the items. I've added a helper Add method that allows a simpler usage:

using (var disposables = new DisposableList())
{
    var file = disposables.Add(() => File.Create("test"));
    // ...
    var memory = disposables.Add(() => new MemoryStream());
    // ...
    var cts = disposables.Add(() => new CancellationTokenSource());
    // ... 
}

这篇关于嵌套&QUOT处理;采用&QUOT;在C#中的语句的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆