我注意到using我的代码中嵌套语句的级别最近有所增加.究其原因,可能是因为我使用越来越多的async/await格局,这往往增加了至少一个以上using的CancellationTokenSource或CancellationTokenRegistration.
那么,如何减少嵌套using,所以代码看起来不像圣诞树?之前已经提出了类似的问题,我想总结一下我从答案中学到的东西.
using而不缩进.一个假的例子:using (var a = new FileStream())
using (var b = new MemoryStream())
using (var c = new CancellationTokenSource())
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
这可能有用,但通常会有一些代码using(例如,创建另一个对象可能为时过早):
// ...
using (var a = new FileStream())
{
// ...
using (var b = new MemoryStream())
{
// ...
using (var c = new CancellationTokenSource())
{
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
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()))
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
这与上面有相同的限制,加上更加冗长和不易读,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);
// ...
}
Run Code Online (Sandbox Code Playgroud)
[更新]注释中有很多有效点,嵌套using语句确保Dispose将在每个对象上调用,即使某些内部Dispose调用抛出.但是,有一个模糊的问题:除了最外层的框架之外,所有嵌套的"使用"框架可能抛出的嵌套异常都将丢失.更多关于这里.
Mik*_*ray 16
在单一方法中,第一个选项将是我的选择.但是在某些情况下这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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们从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());
// ...
}
Run Code Online (Sandbox Code Playgroud)
你应该总是参考你的假例子.如果不可能,就像你提到的那样,那么很有可能你可以将内部内容重构为一个单独的方法.如果这也没有意义,你应该坚持你的第二个例子.其他所有东西看起来都不那么可读,不太明显,也不太常见.
我会坚持使用块.为什么?
| 归档时间: |
|
| 查看次数: |
9896 次 |
| 最近记录: |