摆脱嵌套的 using(...) 语句
有时我需要在一个函数中使用多个一次性对象。最常见的情况是拥有 StreamReader 和 StreamWriter,但有时甚至不止于此。
嵌套的 using 语句很快就会堆积起来并且看起来很难看。 为了解决这个问题,我创建了一个小类,它收集 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 函数,以便该类更加灵活。 当然,滥用这个函数可能会导致对象没有被正确处理,但是如果没有这个类,还有很多其他的方法会搬起石头砸自己的脚。
Sometimes I need to use several disposable objects within a function. Most common case is having StreamReader and StreamWriter but sometimes it's even more than this.
Nested using statements quickly add up and look ugly.
To remedy this I've created a small class that collects IDisposable objects and disposes of them when it itself is disposed.
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
}
So my code now looks like this:
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
}
Is there anything wrong with this approach that can cause problems down the road?
I left the Remove function inherited from the HashSet on purpose so that the class would be more flexible.
Surely misusing this function can lead to objects not being disposed of properly, but then there many other ways to shoot yourself in the foot without this class.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
你可以这样做:
You could just do this:
关于一般原则的几点:
using
语句而无需额外的大括号如果有两个相同类型的变量,则可以使用单个 using 语句:
现在假设您确实想继续执行此操作:
Add
调用相反的顺序处理内容。HashSet
或任何集合派生。您应该在类中拥有一个列表作为私有成员变量。Dispose
调用失败,则不会进行任何其他Dispose
调用;使用传统的using
语句,每次对Dispose
的调用都是在其自己的finally
块中进行的。基本上,我认为这是一个坏主意。嵌套两层深一点也不痛苦;嵌套三个应该很少见;嵌套四个或更多强烈建议重构。您应该在设计时远离它,而不是试图应对深度嵌套的痛苦。
A few points about the general principle:
using
statements without extra bracesIf you have two variables of the same type, you can use a single using statement:
Now assuming you really want to go ahead with this:
Add
.HashSet<T>
or indeed any collection. You should just have a list within the class as a private member variable.Dispose
calls fails, none of the otherDispose
calls will be made; with a traditionalusing
statement, each call toDispose
is made within its ownfinally
block.Basically, I think it's a bad idea. Nesting two levels deep is far from painful; nesting three should be rare; nesting four or more strongly suggests refactoring. Rather than trying to cope with the pain of deep nesting, you should design away from it.
也许您只是展示了一个简单的示例,但我认为以下内容更具可读性。
Maybe it is just that you have shown a simple example, but I think the following is more readable.
您可以仅使用一对大括号来使嵌套的
using
语句更漂亮,如下所示:要回答您的问题,您需要以相反的加法顺序进行处理。
因此,您不能使用
HashSet
。此外,没有理由向外界公开
IDisposable
列表。因此,您不应继承任何集合类,而应维护一个私有
List
。然后,您应该具有公共
Add
和Dispose
方法(并且没有其他方法),并在Dispose
中向后循环列表。You can make nested
using
statements prettier by only using one pair of braces, like this:To answer your question, you need to dispose in the opposite order of addition.
Therefore, you cannot use a
HashSet
.Also, there is no reason to expose the list of
IDisposable
s to the outside world.Therefore, you should not inherit any collection class, and instead maintain a private
List<IDisposable>
.You should then have public
Add<T>
andDispose
methods (and no other methods), and loop through the list backwards inDispose
.就我个人而言,这会让我发疯。如果您发现嵌套的 using 语句很烦人,您可以恢复到 try/finally 语法。 Dispose 方法不应抛出异常,因此您可以假设多个一次性对象不需要单独包装在 try/finally 块中。
另外值得注意的是,您只需要一组括号来表示相邻的 using 块,例如:
Personally this would drive me nuts. If you are finding nested using statements to be annoying, you could revert to the try/finally syntax. Dispose methods are not supposed to throw exceptions so you could assume that multiple disposables would not need to be individually wrapped in try/finally blocks.
Also worth noting is that you only need one set of brackets for adjacent using blocks like:
我不得不说,我不同意那些想要一个接一个地使用语句的人,例如:
在我看来,这是非常不可读的 - 任何未包装的代码块都是糟糕的风格,并且可能会导致问题,除非所有从事此工作的开发人员都非常小心。
我会将其与以下内容相提并论:
从技术上讲,它并不是无效的,但看起来很糟糕。
我同意 MultiDispose 概念可能不是最好的想法,因为它不是一种可接受的模式,但我也绝对不会走这条路。如果您无法将事物分解成更小的块,那么我建议您只使用嵌套的用法。
I've got to say I disagree with the people who want to do using statements one after the other like:
In my opinion, that's very unreadable - having any block of code that's not wrapped is just bad style, and may lead to problems unless all developers working on it are very careful.
I'd put that on par with something like:
Technically it's not invalid, but it's awful to look at.
I agree that the MultiDispose concept is probably not the best idea, due to the fact that it's not an accepted pattern, but I definitely wouldn't go this route either. If you can't break things up into smaller chunks, then I'd suggest just living with the nested usings.