开发者

A problematic example of the IDisposable pattern?

Say you have 3 classes that implement IDisposable - A, B and C. Classes A and B are both dependent on class C.

  1. Would it be correct to say that classes A and B's typical implementation of Dispose() would be:

    public void Dispose()
    {
        if (m_C != null) m_C.Dispose();
    }
    
  2. If there's an instance of A and and instance of B that share the same instance of C, how would you overcome the problem that disposing an instance of A would damage 开发者_如何转开发the instance of B?

  3. Last minute addendum - If in point 2 it's a DI container that instantiates all instances, who is responsible for disposing of the objects? Is it the container itself? How?

Thanks, urig


The dispose pattern relies on there being a well-established "owner" who gets to decide when the resource should be disposed of.

If A and B need to refer to the same instance of C, then only one of them should act as "owner".

While you can do what amounts to reference counting, I usually find it's better to just document who "owns" what. For example, when you create a Bitmap with a stream, from that point on the Bitmap owns the stream, and you shouldn't dispose it yourself. This can cause a few issues, but it's ultimately simpler than trying to dredge up reference counting.


Doing a null check won't help as if B disposes of C this won't update A's reference.

You have to ensure that only one of the classes has ownership of C. This owner class is then responsible for its disposal.

Generally the class that creates C should be the class that disposes of it.


Only one instance must be the owner, and it is responsible for disposing. Non-owner instance should get C reference using function like Attach, and it should not dispose it.


Last minute addendum - If in point 2 it's a DI container that instantiates all instances, who is responsible for disposing of the objects? Is it the container itself? How?

Yes, the container owns any IDisposable objects it creates. The container disposes these objects when it is disposed itself. All DI containers should already do this by default.

Sometimes the DI framework gives you a way to take ownership. For example, in Autofac you can ask for an Owned<T> to be injected, and then you can safely call Owned<T>.Dispose() yourself when you're done with the object. This is especially useful if you're dynamically creating instances via an injected Func<Owned<T>> factory. Note that such "owned instances" are not intended to be shared.


Who created the instance? This is generally the owner and should be responsible for Disposing of the instance.

Chances are you have an "outer" class that created C and then passed it, directly or indirectly, into A and B. This is probably the natural candidate who has responsibility for the lifecycle of C, and should be disposing of it.

[Edit: in reponse to OP's comment] It sounds like maybe you should have another look at the design here. Is this pointing to a refactor being needed?

You have a class C which needs disposing, that is used by both A and B; should you have a class who has overall responsibility for marshalling C through A and B, rather than having them create C from the DI container themselves? Or is C really more of a singleton. Does it even really need disposing?

I guess all I'm saying is that this feels like it may point to a design that needs a bit of a change; have another look with a critical eye.


Two methods I could think would be to:

  1. Create a parent collection within C, and in the dispose method of A and B, remove self from the child's parent collection. Then if the parent collection count is 0, call dispose.
  2. Lazy load a property within both A and B to access C. Perform a null check on C, if some other object has destroyed it, reinstantiate it (if possible).


  1. I usually do it this way, tends to be accepted and it definitely works. However if another object has disposed it, null checking won't stop dispose being called again because it won't be null. C's disposal should defend against multiple calls however.

  2. Very good question. Immediately what springs to mind is there needs to be logic to know how many counts are currently on that object, so it's own disposal routines can defend itself. Semaphores do this, but are heavy.

I'd also question where you'd see this in a real-world example, might be a design discrepancy. Update: as others have mentioned, it's coming down to a design issue - you get a similar effect when using CCWs, someone else releases the underlying COM object where other users might still use it.


That would be a correct implementation: however, you might want to save references to all objects depending on a specific instance of C in both A and B, and have a check for that list to be empty (except for the currently disposing object) in C's Dispose method.


In addition to what Jon said - the creator is the owner and should dispose the Disposable.

In this case it's the container, and the container is responsible for disposing the components. Not every component supports this (or at least not every one fully). Castle Windsor does. Also Autofac supports it.


Every IDisposable object should have one owner. If a resource will need to be shared among multiple users, any of which might be the last to use it, then each user should hold a reference to its own wrapper. The wrapper objects should then use some means other than IDisposable to coordinate with a single privately-created inner-wrapper object which will then call Dispose on the resource. The inner wrapper object doesn't need to use IDisposable for cleanup because it's not exposed publicly, and the fact that it doesn't have to use IDisposable means that it can use a means of cleanup that accommodates multiple owners.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜