我注意到我的代码中嵌套using
语句的层级最近有所增加。原因可能是我越来越多地使用async/await
模式,这通常会至少再添加一个using
用于CancellationTokenSource
或CancellationTokenRegistration
。
那么,如何减少using
的嵌套,以使代码看起来不像圣诞树?类似的问题在Stack Overflow上已经被问过了,我想总结一下我从答案中学到的内容。
使用相邻的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
),放入单个using
中,例如:
// ...
FileStream a = null;
MemoryStream b = null;
CancellationTokenSource c = null;
// ...
using (IDisposable a1 = (a = new FileStream()),
b1 = (b = new MemoryStream()),
c1 = (c = new CancellationTokenSource()))
{
// ...
}
这跟上面的限制一样,而且更加啰嗦,阅读起来也不太流畅,在我看来。
将该方法重构成几个方法。
就我所知,这是一种首选方式。但我很好奇,为什么下面的方法会被认为是一种坏习惯呢?
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
调用会抛出异常。然而,还存在一个有点难以理解的问题:除了最外层的异常之外,所有由于处理嵌套 'using' 帧而可能抛出的嵌套异常都将丢失。更多信息请参见这里。
using
语句,那么你的方法可能已经有点太复杂了,因此需要进行重构。如果你更或多或少地遵循“干净代码”原则,通常不会出现太多嵌套的using
语句。 @MuctadirDinar:同样的想法! - alzaimarDispose
抛出异常,则存在永远无法处理某些对象的可能性。 - Alexei Levenkov