c# - 在 C# 中处理嵌套的 "using"语句

标签 c#

我注意到我的代码中嵌套的 using 语句的级别最近有所增加。原因可能是因为我使用越来越多的async/await pattern,对于CancellationTokenSource,经常至少多加一个using >CancellationTokenRegistration.

那么,如何减少using的嵌套,让代码看起来不像圣诞树呢?之前在SO上问过类似的问题,我想总结一下我从答案中学到的东西。

使用相邻的 using 不缩进。一个假的例子:

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

这可能有效,但通常在使用之间有一些代码(例如,创建另一个对象可能为时过早):

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

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

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

这与上面的限制相同,而且更加冗长且可读性差,IMO。

将方法重构为几个方法。

据我所知,这是一种首选方式但是,我很好奇,为什么以下内容会被视为不良做法?

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);
    // ... 
}

[更新] 评论中有很多有效的观点,嵌套 using 语句确保 Dispose 将在每个对象上调用,即使某些内部 Dispose 调用抛出。然而,有一个有点模糊的问题:除了最外层的异常外,所有可能因处理嵌套的“使用”框架而抛出的嵌套异常都将丢失。关于此的更多信息 here .

最佳答案

在单个方法中,第一个选项是我的选择。但是在某些情况下,DisposableList 很有用。特别是,如果您有许多一次性字段都需要处理掉(在这种情况下您不能使用 using)。给出的实现是好的开始,但它有一些问题(Alexei 在评论中指出):

  1. 要求您记住将项目添加到列表中。 (虽然你也可以说你必须记住使用 using。)
  2. 如果其中一种处置方法抛出异常,则中止处置过程,留下未处置的剩余项目。

让我们解决这些问题:

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。我添加了一个帮助程序 Add 方法,它允许更简单的用法:

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

关于c# - 在 C# 中处理嵌套的 "using"语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19217734/

相关文章:

c# - 使用 Html.HiddenFor 将模型传递给 Controller

c# - ListView 调整列大小性能问题(分组)

c# - javascript 服务器标签格式不正确

c# - 您如何使用 .net 2.0 中的 WebBrowser 控件检查 ajax 更新?

C# 正则表达式部分字符串匹配

c# - 从方法返回 <list> 值

c# - XML 解析错误 - 使用 C# 的 Salesforce 批量 API

c# - 在单元测试中验证 Autofac 注册

C# HttpClient 损坏的管道,但 curl 有效

c# - 亚马逊 MWS 的产品 Feed