有时候我需要在一个函数中使用几个一次性对象。最常见的情况是使用StreamReader和StreamWriter,但有时它甚至比这更多。
嵌套使用语句快速加起来并且看起来很难看。 为了解决这个问题,我创建了一个小类来收集IDisposable对象,并在它本身被处理时处理它们。
public class MultiDispose : HashSet<IDisposable>, IDisposable
{
public MultiDispose(params IDisposable[] objectsToDispose)
{
foreach (IDisposable d in objectsToDispose)
{
this.Add(d);
}
}
public T Add<T>(T obj) where T : IDisposable
{
base.Add(obj);
return obj;
}
public void DisposeObject(IDisposable obj)
{
obj.Dispose();
base.Remove(obj);
}
#region IDisposable Members
public void Dispose()
{
foreach (IDisposable d in this)
{
d.Dispose();
}
}
#endregion
}
所以我的代码现在看起来像这样:
using (MultiDispose md = new MultiDispose())
{
StreamReader rdr = md.Add(new StreamReader(args[0]));
StreamWriter wrt = md.Add(new StreamWriter(args[1]));
WhateverElseNeedsDisposing w = md.Add(new WhateverElseNeedsDisposing());
// code
}
这种方法有什么问题可以导致问题吗? 我故意离开了从HashSet继承的Remove函数,以便该类更灵活。 当然滥用这个功能可能导致物体没有被正确处理掉,但是在没有这个类的情况下,还有很多其他方法可以射击自己。
答案 0 :(得分:47)
你可以这样做:
using (var a = new A())
using (var b = new B())
{
/// ...
}
答案 1 :(得分:20)
关于一般原则的几点:
using
语句如果您有两个相同类型的变量,则可以使用单个using语句:
using (Stream input = File.OpenRead("input.dat"),
output = File.OpenWrite("output.dat"))
{
}
现在假设你真的想继续这个:
Add
的调用相反的顺序处理事务。HashSet<T>
或任何集合派生。您应该在类中将列表作为私有成员变量。Dispose
调用失败,则不会进行任何其他Dispose
调用;使用传统的using
语句,每次调用Dispose
都会在其finally
块中进行。基本上,我认为这是一个坏主意。深度嵌套两层远非痛苦;筑巢三应该是罕见的;嵌套四个或更多强烈建议重构。你应该远离它,而不是试图应对深层筑巢的痛苦。
答案 2 :(得分:13)
也许只是你展示了一个简单的例子,但我认为以下内容更具可读性。
using (StreamReader rdr = new StreamReader(args[0]))
using (StreamWriter wrt = new StreamWriter(args[1]))
{
// code
}
答案 3 :(得分:7)
您可以通过仅使用一对大括号使嵌套的using
语句更漂亮,如下所示:
using (StreamReader rdr = new StreamReader(args[0]))
using (StreamWriter wrt = new StreamWriter(args[1]))
{
///...
}
要回答你的问题,你需要按照相反的顺序处理
因此,您无法使用HashSet
。
此外,没有理由将IDisposable
列表公开给外界
因此,您不应继承任何集合类,而应维护私有List<IDisposable>
。
然后,您应该使用公开Add<T>
和Dispose
方法(而不是其他方法),并在Dispose
中向后循环列表。
答案 4 :(得分:4)
就个人而言,这会让我疯狂。如果您发现嵌套的using语句很烦人,则可以恢复为try / finally语法。 Dispose方法不应该抛出异常,因此你可以假设多个一次性用品不需要单独包装在try / finally块中。
另外值得注意的是,您只需要一组括号用于相邻的使用块,例如:
using (var stream = File.Open(...))
using (var reader = new StreamReader(stream)) {
// do stuff
}
答案 5 :(得分:2)
我必须说我不同意那些想要一个接一个地使用陈述的人:
using (var a = new StreamReader())
using (var b = new StreamWriter())
{
// Implementation
}
在我看来,这是非常难以理解的 - 任何未被包装的代码块都只是糟糕的样式,并且可能会导致问题,除非所有开发人员都非常小心。
我把它与以下内容相提并论:
if (someBoolean) DoSomething();
{
// Some code block unrelated to the if statement
}
从技术上讲,它并非无效,但看起来很糟糕。
我同意MultiDispose概念可能不是最好的主意,因为它不是一个可接受的模式,但我绝对不会走这条路。如果你不能将事情分解成更小的块,那么我建议只使用嵌套的使用。