views:

304

answers:

4

For example:

Queue<System.Drawing.SolidBrush> brushQ = new Queue<System.Drawing.SolidBrush>();
...
brushQ.Clear();

If I don't explicitly dequeue each item and dispose of them individually, do the remaining items get disposed when calling Clear()? How about when the queue is garbage collected?

Assuming the answer is "no", then what is the best practice? Do you have to always iterate through the queue and dispose each item?

That can get ugly, especially if you have to try..finally around each dispose, in case one throws an exception.

Edit

So, it seems like the burden is on the user of a generic collection to know that, if the items are Disposable (meaning they are likely to be using unmanaged resources that won't be cleaned up by the garbage collector), then:

  1. When you remove an item from the collection, make sure you Dispose() it.
  2. DON'T CALL Clear(). Iterate through the collection and dispose of each item.

Maybe the documentation for the generic collections should mention that.

+2  A: 

When do you expect them to be disposed? How will the collection know if there are other references to the objects in it?

On Freund
+4  A: 

The generic collections don't actually know anything about the type of object they contain, so calling Clear will not cause them to call Dispose() on the items. The GC will eventually dispose of them once the collection itself gets disposed of, provided nothing else has an active reference to one of those items.

If you want to ensure that the objects have their Dispose method called when you call Clear on the collection you would need to derive your own collection and override the appropriate methods and make the calls yourself.

Scott Dorman
"The GC will eventually dispose of them..."It is my understanding that the GC does not call Dispose. It will clean up the managed resources, but any unmanaged resources will remain.
mbeckish
Incorrect. When the GC does eventually get around to freeing memory, if the object is IDisposable, the GC will dispose it. You just have no guarantee as to when that happens, unless the line after brushQ.Clear(); is GC.Collect();.
GWLlosa
What about this: http://stackoverflow.com/questions/45036/will-the-gc-call-idisposable-dispose-for-me (see "Will the GC call IDisposable.Dispose for me?" in this page's right panel).
mbeckish
@GWLlosa: That's not entirely true. If the object implements IDisposable the GC will eventually collect it as long as there are no other references to it. If there is still a live reference to that object, it won't get collected just because it's container has been collected.
Scott Dorman
@mbeckish: What's your question with respect to that thread? Having the GC dispose of the objects isn't necessarily the same thing as you calling Dispose() yourself, and, in fact, the GC won't actually call Dispose() directly.
Scott Dorman
You and GWLlosa both say that the GC will dispose (i.e. get rid of *unmanaged* resources) items when they are collected. I don't believe this is true, and I think that the thread above supports that.
mbeckish
@mbeckish I never said anything about managed or unmanaged resources. The GC will *never* clean up unmanaged resources under any circumstances (unless it's doing so through a finalizer) as it doesn't know anything about them. That's the reason for implementing and calling Dispose() yourself.
Scott Dorman
See also:"Although the garbage collector is able to track the lifetime of a managed object that encapsulates an unmanaged resource, it does not have specific knowledge about how to clean up the resource."http://msdn.microsoft.com/en-us/library/f144e03t.aspx
mbeckish
@mbeckish Correct. That supports my previous comment.
Scott Dorman
@Scott Dorman "The GC will eventually dispose of them once the collection itself gets disposed of..." I interpreted your use of "dispose" to mean that either the GC explicitly calls Dispose(), or somehow knows how to clean up the unmanaged resources. Sorry if that is not what you meant.
mbeckish
@mbeckish Nope, wrong interpretation...but that's the whole point of being able to leave comments. :) Sounds like it's clearer for you now.
Scott Dorman
The GC never calls dispose. It will, however, call a finalizer if you have one for your type.
On Freund
A: 

As others have said, it won't happen. However, you could build your own extension method to dispose them if you want.

Joel Coehoorn
+1  A: 

Let me see if I can write an example code for this one.

Edit:

The following code implements an IDisposable Queue:

class DisposableQueue<T>:Queue<T>,IDisposable where T:IDisposable

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public virtual void Dispose(bool disposing) {
        if (disposing) {
            foreach (T type in this) {
                try
                {
                    type.Dispose();
                }
                finally {/* In case of ObjectDisposedException*/}
            }
        }
    }
    #endregion
}
Igor Zelaya
What if you are designing your own generic queue derivative for a library, but you don't if T will be disposable or not. Do you need to fork your design into two versions, DisposableQueue and NondisposableQueue? Or is there a way at runtime to determine if T is disposable?
mbeckish
Also, this is assuming that no one holds onto a reference to your items. As mentioned in other posts, I guess that's not a good assumption for a general purpose queue.
mbeckish
@mbeckish - The normal Queue is a "NonDisposable" one. putting code like if (type.GetType() == typeof(IDisposable))Does not sound like a solid programming pattern.
Igor Zelaya
@mbeckish - the whole point of having IDisposable objects is to free resources as soon as you stop using them. It is up to your code to check if this resources are not in use anymore.
Igor Zelaya
I agree. So, it seems like the burden should be put on the consumer of your queue to Dispose items as they are dequeued, and also loop through the queue and Dispose any remaining items when you are done with the queue. It's just that this can get ugly for the developer using your queue.
mbeckish
@mbeckish - You are right on that disposing all objects in a the Clear method can get ugly. A safer way would be just not to override that method.
Igor Zelaya
But it is ugly for the developer using your queue, too. Instead of calling Clear(), I have to loop through the queue, dequeue, Dispose, and somehow encapsulate all of this in try..finally blocks to make sure everything gets disposed.
mbeckish
By "your queue", I meant "any generic queue of iDisposables", not just the queue you implemented in this answer.
mbeckish
A better way would be to not constrain T to IDisposable and instead explicitly cast type as IDisposable in the foreach loops. If the "as" returns null, then the type doesn't implement IDisposable and there is no need to call it, otherwise you explicitly call Dispose() through the interface.
Scott Dorman