我注意到我的代码中嵌套的 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 在评论中指出):
- 要求您记住将项目添加到列表中。 (虽然你也可以说你必须记住使用
using
。) - 如果其中一种处置方法抛出异常,则中止处置过程,留下未处置的剩余项目。
让我们解决这些问题:
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/