设计困境:谁应该处理一次性参数?
如果我的类在其构造函数中使用一次性资源(DbConnection
,如果重要的话),我应该在我的类中实现 IDisposable
并处置 DbConnection
对象,还是让用户句柄处置DbConnection
?
目前,我在类中实现了 IDisposable,但现在我看到了一些可能的负面影响:类设计混乱,如果使用不正确,则会双重处置 DbConnection
。但也有积极的一面:使用更简单是主要的(特别是如果您使用多个一次性参数)。
在“狂野”中,我看到了两种方法,所以我无法决定。
更新: 谢谢大家的回答,事实上,这表明有时这确实不是一个简单的选择。而且也很难选择正确的答案。不过,我决定将来坚持使用最简单的方法。所以最终的选择是:不实现IDisposable。
If my class uses disposable resource in it's constructor (DbConnection
if it matters) should I implement IDisposable
in my class and dispose DbConnection
object, or let user handle disposal of DbConnection
?
Currently I implement IDisposable in my class, but now I see some possible negative effects: clutters class design, double disposal of DbConnection
if used incorrectly. But there are also positive ones: simplier use is the major one (especially if you use multiple disposable parameters).
In the "wild" I see both approaches, so I can't decide..
Update:
Thanks everyone for answers, which, in fact, showed that it's indeed not an simple choice sometimes. And it's hard to pick correct answer too. However I decided to stick to most simple one to follow in future. So final choice is: do not implement IDisposable.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
它应该由创建它的人处理——与其创建的范围相同。您创建了一个对象,您负责处理它。就这么简单。
It should be disposed by whoever created it - the same scope as its creation. You create an object, you're responsible for disposing it. Simple as that.
在构造函数中使用一次性资源并不是最佳选择,并且可能会导致奇怪的情况。相反,您应该将
DbConnectionFactory
注入能够按需创建连接的构造函数,您可以使用它们并在方法内部处理它们。如果由于某种原因该模式对于您的场景来说不合逻辑。下一个最佳选择是仍然将一次性资源的使用移出构造函数,并让方法返回一次性且满足您的目的的新类型。
这类似于您的服务类型类返回需要从方法中处理的数据读取器。 (理论上它可能是一个真正的数据读取器)
Using a disposable resource in a constructor is non-optimal and can lead to strangeness. You should instead be injecting a
DbConnectionFactory
into the constructor that is capable of creating connections on demand that you can use them and dispose them internally inside your methods.If for some reason that pattern would be illogical for your scenario. The next best option would be to still move the usage of the disposable resource out of the constructor and have a method return a new type that is disposable and serves your purpose.
This would be similar to your service type class returning a data reader that needs disposed from a method. (Theoretically it could be a true data reader)
我会或者:
IDisposable
;调用代码负责处理传入的对象并确保它至少与新对象一样长。或者:
IDisposable
;调用代码放弃传入对象的所有权,并应将其视为已处置。并且清楚地记录我所做的选择。恕我直言,试图在多个地方拥有一次性物品很可能有一天会出错。
选项 1 适用于您打算将一次性对象传递到几个新对象中的场景,这些新对象将共享对其的访问(数据库连接听起来可能是这样的)。选项 2 适用于当您正在做的事情更像是将一次性对象包装在具有更多(或更抽象)功能的另一个对象中时,特别是当您要返回它并且不控制其寿命时。
I would either:
IDisposable
; the calling code is responsible for both disposing of the passed-in object and ensuring it lives at least as long as the new object.Or:
IDisposable
; the calling code relinquishes ownership of the passed-in object and should treat it as already disposed.And clearly document which choice I made. Attempting to have the disposable object owned in multiple places is too likely to go wrong one day, IMHO.
Option 1 is for the scenario where you intend to pass the disposable object into several new objects, which will share access to it (a database connection sounds like it might be this). Option 2 is for when what you're doing is more like wrapping up the disposable object in another object with more (or more abstract) functionality, especially if you're returning it and so don't control its lifespan.
在某些情况下,对象将使用一次性资源,其使用寿命超过使用它的新对象的使用寿命。在其他情况下,人们会期望将一个物体交给一个对其内的任何一次性物品一无所知的实体。某些类型的对象可能会在这两种情况下使用。
考虑一个假设的 IDisposable SoundPlayer 类型,它将播放 IDisposable SoundSource 对象,然后在完成后自行处理。很容易想象这样的情况:人们希望能够加载一次 SoundSource,播放多次,然后手动处理它。也很容易想象这样的情况:人们想要加载一个 SoundSource 对象,然后“解雇并忘记”播放器。 SoundSource 的创建者在玩家完成之前无法处置它,但之后就不再使用它,因此最方便的做法是由 SoundPlayer 处置它。
我建议,如果这两种情况至少都是合理的,您应该提供一个工厂方法来转移 IDisposable 的所有权以及一个不转移所有权的工厂方法,或者您应该有一个工厂方法,其中包含一个参数来指示所有权是否应该是转移。请注意,将 IDisposables 传递给应该使用和处理它们的类的构造函数是危险的,因为 Microsoft 不允许在从字段初始值设定项调用的构造函数周围使用 try/catch 块,并且很难确保在构造函数抛出时处理事物。使用工厂方法调用构造函数可以使错误捕获变得更加容易(尽管这仍然很痛苦——尤其是在 C# 中)。
编辑--附录
有时有用的另一种情况方法是让一个对象发布 Dispose 事件或在其构造函数中接受委托,以便在不再需要传入的对象时运行。我更喜欢事件的显式委托,因为使用事件意味着事件订阅者(创建将暂时保留可处置资源的对象的人)有义务取消订阅,这会增加额外的复杂性,特别是如果对象可能是交给另一个对象。由于到目前为止,当对象的接收者不再需要该对象时,调用者希望做的最常见的事情就是简单地删除该对象,因此最简单的情况是简单地为对象接收者提供一个选项来自行处理处置。
There are some situations where an object will use a Disposable resource whose useful lifetime exceeds that of the new object that used it. There will be other situations where one will expect to hand off an object to an entity which knows nothing about any disposable items within it. Some types of object may find themselves used in both scenarios.
Consider a hypothetical IDisposable SoundPlayer type which will play an IDisposable SoundSource object and then dispose of itself when it's done. It's easy to imagine situations where one would want to be able to load a SoundSource once, play it multiple times, and then manually dispose of it afterward. It's also easy to imagine situations where one would want to load a SoundSource object and then "fire and forget" the player. The creator of the SoundSource can't Dispose it until the player's done, but will have no use for it afterward, so the most convenient course of action is for the SoundPlayer to dispose of it.
I would suggest that if both scenarios are at least plausible, you should either provide a factory method which transfers ownership of the IDisposable along with one that doesn't, or else you should have a factory method with a parameter that indicates whether ownership should be transferred. Note that passing IDisposables to constructors for classes that are supposed to consume and Dispose them is dangerous because Microsoft does not allow try/catch blocks around constructors invoked from field initializers and it's difficult to ensure things get disposed when a constructor throws. Having a factory method call the constructor makes error trapping much easier (though it's still a pain--especially in C#).
Edit--Addendum
Another situation approach which is sometimes useful is to have an object either publish a Disposed event or accept a delegate in its constructor to be run when the passed-in object is no longer needed. I prefer the explicit delegate to the event, since using an event would imply that the event subscriber (the person creating the object that will temporarily hold the disposable resource) has an obligation to unsubscribe, which adds additional complication, especially if the object might be handed off to yet another object. Since by far the most common thing the caller will want to have done when the recipient of the object no longer needs it is for the object to simply be deleted, the simplest case is to simply have an option for the object recipient to handle disposal itself.