用托管语言释放对象的模式
在复杂的应用程序中(涉及控制反转和相当多的类),几乎不可能知道何时不再引用某个对象。
第一个问题:建议上面的陈述,即此类应用程序存在设计缺陷,因为有一种模式说:“在所有 OO 编程中,它都是关于使用其他类型的对象来简化实现的对象。但是:对于创建的任何对象应该有一个主人来照顾它的一生。”
我认为传统的非托管 OO 编程的工作方式与上面所述是一样的:某些所有者最终将释放/释放所使用的对象。
然而,托管语言的好处是原则上您不必再关心生命周期管理。只要一个对象以任何方式(事件处理程序...)并且从任何地方(可能不是“所有者”)被引用,它就会存在并且应该存在,因为它仍在使用中。
我真的很喜欢这个想法,而且你不必考虑所有者关系。然而,在程序中的某个时刻,您可能会明显地想要删除一个对象(或者至少以某种方式将其静音,因为它不会在那里)。
IStoppable:设计模式的建议 可能有一个像“IStoppable”这样的接口,带有“Stop()”方法和“Stopped”事件,以便使用它的任何其他对象都可以删除对该对象的引用。 (因此,如果可能的话,需要在事件处理程序中拔出 OnStopped 事件处理程序)。因此,不再需要该对象并将被收集。
也许这很天真,但我愿意相信这个想法是对象不会有未定义的状态。即使某个其他对象错过了在 OnStopped 上注销自身,它也将保持活动状态并且仍然可以被调用。仅仅删除大部分引用就不会破坏任何东西。
我认为这种模式可以被视为一种无政府主义的应用程序设计,因为
- 它基于这样的想法:任何其他对象都可以管理 Istoppable 的生命周期
- ,不需要所有者
- ,可以认为可以放弃取消注册的决定从IStoppable到那些使用它的人,
- 你不需要处置、销毁或扔掉——你只需停下来,让它活下去(让GC来做肮脏的部分)
IDisposable:从scatch开始,只是为了检查一个相关模式: 一次性模式建议您仍然应该像非托管 OO 编程一样思考和工作:处置不再需要的对象。
- 使用是您在方法中的朋友(非常舒服!),
- 否则自己的 IDisposable 实现是您的朋友。
- 在使用它/调用 Dispose 之后你不应该再调用它:未定义的行为。
- 以实现和资源为中心:它不是关于何时和为什么,而是更多关于回收资源的细节
所以再说一遍:在一个应用程序中,我不知道除了“所有者”之外是否还有其他东西指向一个对象,很难确保没有人会再引用和调用它。
我在 .NET 的 Component 类中读到了一个“Dispose”事件。它周围有设计模式吗?
为什么我要考虑一次性用品?我为什么要这么做? 在受管理的世界中...
谢谢! 塞巴斯蒂安
In a complex application (involving inversion of control and quite some classes) it is hardly possible to know when a certain object won't be referenced anylonger.
First Question: Suggests the statement above that there is a design flaw in such an application, since there is a pattern saying: "In all OO programming it is about objects using other types of objects to ease up implementation. However: For any object created there should be some owner that will take care of its lifetime."
I assume it is save to state that traditional unmanaged OO programming works like stated above: Some owner will eventually free / release the used object.
However the benefit of a managed language is that in principle you don't have to care about lifetime management anymore. As long an object is referenced anyhow (event-handler...) and from anywhere (maybe not the "owner") it lives and should live, since it is still in use.
I really like that idea and that you don't have to think in terms of owner relationships. However at some point in a program it might get obvious that you want to get rid of an object (or at least mute it in a way as it wouldn't be there).
IStoppable: a suggestion of a design pattern
There could be an interface like "IStoppable", with a "Stop()" method and an "Stopped" event, so that any other object using it can remove their references onto the object. (Therefore would need to unplug their OnStopped event handler within the event handler if that is possible). As a result the object is no longer needed and will get collected.
Maybe it is naive but what i like to believe about that idea is that there wouldn't be an undefined state of the object. Even if some other object missed to unregister itself on OnStopped it will just stay alive and can still get called. Nothing got broken just by removing most references onto it.
I think this pattern can be viewed as an anarchistic app design, since
- it is based on the idea that ANY other object can manage the lifetime of an IStoppable
- there is no need for an owner
- it would be considered as OK to leave the decision of unregistering from an IStoppable to those using it
- you don't need to dispose, destroy or throw away - you just stop and let live (let GC do the dirty part)
IDisposable: from scatch and just to check a related pattern:
The disposable pattern suggests that you should still think and work like in unmanaged OO programming: Dispose an object that you don't need anylonger.
- using is your friend in a method (very comfortable!)
- an own IDisposable implementation is your friend otherwise.
- after using it / calling Dispose you shouldn't call it anylonger: undefined behaviour.
- implementation and resource centric: it is not so much about when and why, but more about the details of reclaiming resources
So again: In an application where i don't have in mind if anything else but an "owner" is pointing to an object, it is hard to ensure that noone will reference and call it anylonger.
I read of a "Dispose" event in the Component class of .NET. Is there a design pattern around it?
Why would i want to think in terms of Disposables? Why should i?
In a managed world...
Thanks!
Sebastian
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我个人不喜欢上面定义的
IStoppable
的想法。您说您希望任何对象来管理该对象的生命周期 - 然而,定义的生命周期实际上表明了所有权 - 允许多个对象管理单个对象的生命周期将导致长期IDisposable 是 .NET 世界中定义良好的模式。我写了关于实现 IDisposable 的整个系列 这是对其用法的一个很好的介绍。然而,它的目的是处理具有非托管组件的资源 - 当您有一个引用本机资源的托管对象时,通常需要对该资源的生命周期进行显式控制。
IDisposable
是用于处理这种情况的已定义模式。话虽如此,如果您无法调用
Dispose()
,正确实现 IDisposable 仍会清理您的资源。缺点是资源将在对象终结期间被清理,这可能发生在对象不再使用后的任意时刻。由于多种原因,这可能会非常糟糕 - 特别是当您使用本质上有限的本机资源时。如果不立即释放资源,您可能会在 GC 在对象上运行之前耗尽资源,尤其是在系统中没有很大内存压力的情况下。I personally don't like the idea of
IStoppable
, as defined above. You're saying you want any object to manage the lifetime of the object - however, a defined lifecycle really suggests ownership - allowing multiple objects to manage the lifetime of a single object is going to cause issues in the longIDisposable
is, however, a well defined pattern in the .NET world. I wrote an entire series on implementing IDisposable which is a decent introduction to it's usage. However, it's purpose is for handling resource which have an unmanaged component - when you have a managed object that refers to a native resource, it's often desirable to have explicit control of the lifetime of that resource.IDisposable
is a defined pattern for handling that situation.That being said, a proper implementation of
IDisposable
will still clean up your resources if you fail to callDispose()
. The downside is that the resource will be cleaned up during the object's finalization, which could occur at any arbitrary point after the object is no longer used. This can be very bad for quite a few reasons - especially if you're using native resources that are limited in nature. By not disposing of the resource immediately, you can run out of resources before the GC runs on the object, especially if there isn't a lot of memory pressure in the system.好吧,首先我要指出一些我对你的“不可阻止的”建议感到不舒服的地方。
IStoppable 引发事件 Stopped,消费者必须了解这一点并发布引用。这充其量是有点复杂,最坏的情况是有问题。消费者必须知道每个引用在哪里,以便删除/重置引用。
您声称“...仅删除其上的大多数引用就不会破坏任何内容。”。这完全取决于实现 Istoppable 的对象及其用途。举例来说,我的 Istoppable 对象是一个对象缓存。现在我忘记或忽略了该事件,突然间我使用了与世界其他地方不同的对象缓存...也许可以,也许不行。
事件是提供此类行为的一种可怕的方式,因为事实证明异常难以处理。当 10 个事件处理程序中的第三个在 Istoppable.Stopped 事件中抛出异常时,这意味着什么?
我想你想表达的是一个可能被许多事物“拥有”并且可以被一个事物强行释放的对象?在这种情况下,您可能会考虑使用引用计数器模式,更像老式的 COM。这当然也存在问题,但在受管理的世界中这些问题不那么严重。
对象周围的引用计数器的问题是您会回到无效/未初始化对象的想法。解决此问题的一种可能方法是为引用计数器提供有效的“默认”实例(或工厂委托),以便在所有引用都已释放且有人仍需要实例时使用。
Ok first I would point out a few things I find uncomfortable about your IStoppable suggestion.
IStoppable raises event Stopped, consumers must know about this and release references. This is a bit complex at best, problematic at worst. Consumers must know where every reference is in order to remove/reset the reference.
You claim "... Nothing got broken just by removing most references onto it.". That entirely depends on the object implementing IStoppable and it's uses. Say, for example, my IStoppable object is an object cache. Now I forget about or ignore the event and suddenly I'm using a different object cache as the rest of the world... maybe that is ok, maybe not.
Events are a horrible way to provide behavior like this due to the fact that exceptions prove difficult to handle. What does it mean when the third out 10 event handlers throws an exception in the IStoppable.Stopped event?
I think what your trying to express is an object that may be 'owned' by many things and can be forcefully released by one? In this case you might consider using a reference counter pattern, more like old-school COM. That of course has issues as well, but they are less of a problem in a managed world.
The issue with a reference counter around an object is that you come back to the idea of an invalid/uninitialized object. One possible way to solve this is to provide the reference counter with a valid 'default' instance (or a factory delegate) to use when all references have been release and someone still wants an instance.
我认为你对现代面向对象语言有误解;特别是范围和垃圾收集。
对象的生命周期很大程度上由它们的范围控制。范围是否仅限于 using 子句、方法甚至应用程序域。
尽管您不一定“关心”对象的生命周期,但编译器会在对象超出范围后立即将其留作垃圾回收。
您可以通过故意告诉垃圾收集器立即运行来加速该过程,但这通常是毫无意义的练习,因为编译器无论如何都会优化代码以在最合适的时间执行此操作。
如果您谈论的是多线程应用程序中的对象,那么这些对象已经公开了停止执行或按需终止它们的机制。
这给我们留下了不受管理的资源。对于这些,包装器应该实现 IDisposable。我将跳过谈论它,因为 Reed Copsey 已经很好地介绍了这一点。
I think you have a misunderstanding of modern OO languages; in particular scope and garbage collection.
The lifetime of the objects are very much controlled by their scope. Whether the scope is limited to a using clause, a method, or even the appdomain.
Although you don't necessarily "care" about the lifetime of the object, the compiler does and will set it aside for garbage collection as soon as it goes out of scope.
You can speed up that process by purposely telling the garbage collector to run now, but that's usually a pointless exercise as the compiler will optimize the code to do so at the most opportune time anyway.
If you are talking about objects in multi-threaded applications, these already expose mechanisms to stop their execution or otherwise kill them on demand.
Which leaves us with unmanaged resources. For those, the wrapper should implement IDisposable. I'll skip talking about it as Reed Copsey has already covered that ground nicely.
虽然有时 Dispose 事件(如 Windows 窗体使用的事件)可能很有用,但事件确实会增加相当多的开销。如果一个对象将保留它曾经拥有的所有 IDisposables 直到它被释放(常见情况),那么最好保留一个 List(Of IDisposable) 并拥有一个私有函数“T RegDisp(T obj),其中 T :IDisposable" 会将一个对象添加到一次性列表中并返回它。不要将字段设置为 SomeDisposable,而是将其设置为 RegDisp(SomeDisposable)。请注意,在 VB 中,如果所有构造函数调用都包装在工厂方法中,则可以在字段初始值设定项中安全地使用 RegDisp(),但在 C# 中无法做到这一点。
顺便说一句,如果 IDisposable 的构造函数接受 IDisposable 作为参数,那么让它接受指示是否将转移该对象的所有权的布尔值通常会很有帮助。如果可能拥有的 IDisposable 将在可变属性(例如 PictureBox.Image)中公开,则属性本身应该是只读的,并具有接受所有权标志的 setter 方法。当对象拥有旧对象时调用 set 方法应该在设置新对象之前释放旧对象。使用该方法将消除对 Dispose 事件的大部分需求。
While there are times a Disposed event (like the one used by Windows Forms) can be useful, events do add a fair bit of overhead. In cases where an object will keep all the IDisposables it ever owns until it's disposed (a common situation) it may be better to keep a List(Of IDisposable) and have a private function "T RegDisp<T>(T obj) where T:IDisposable" which will add an object to the disposables list and return it. Instead of setting a field to SomeDisposable, set it to RegDisp(SomeDisposable). Note that in VB, provided all constructor calls are wrapped in factory methods, it's possible to safely use RegDisp() within field initializers, but that cannot be done in C#.
Incidentally, if an IDisposable's constructor accepts an IDisposable as a parameter, it may often be helpful to have it accept a Boolean indicating whether or not ownership of that object will be transferred. If a possibly-owned IDisposable will be exposed in a mutable property (e.g. PictureBox.Image) the property itself should be read-only, with a setter method that accepts an ownership flag. Calling the set method when the object owns the old object should Dispose the old object before setting the new one. Using that approach will eliminate much of the need for a Disposed event.