When to call Dispose

Z

Zachary Turner

Hello,

The question I have below was originally posted to
microsoft.public.dotnet.framework, as I mistakenly considered this a
question of more general nature. However, the discussion there quickly
turned into one that was very windows forms specific, and it seems
there is some confusion as to the exact answer to my question there
anyway. For those interested, you can see the initial thread here:

http://groups.google.com/group/microsoft.public.dotnet.framework/browse_frm/thread/376ad6d24e835788

The long and short of is that I don't know -when- to call Dispose() on
various types of Windows Forms objects. Do you call it -immediately-
after manually removing a control from a collection, or do you call
after you've removed a control from a collection -for the last time-?
The distinction is important, for example in situations where you may
have a single control that is added and removed multiple times
throughout the life of the application. Of course, you can be
guaranteed correctness by calling Dispose() every time you remove a
control from a collection, and if you need to add it back you create
another identical control with the new() operator. But this is
inefficient, and doges the real question at hand.

I wrote a little test app. It's a single form with two buttons and one
list view. The list view is initialized through the designer to
contain a single column header. The click handlers for the two buttons
are displayed below.

private bool bRemoved = false;
private bool bRemoved2 = false;

private void button1_Click(object sender, EventArgs e)
{
if (!bRemoved)
{
this.Controls.Remove(listView1);
listView1.Dispose();
}
else
this.Controls.Add(listView1);
bRemoved = !bRemoved;
}

private void button2_Click(object sender, EventArgs e)
{
if (!bRemoved2)
{
listView1.Columns.Remove(columnHeader1);
columnHeader1.Dispose();
}
else
listView1.Columns.Add(columnHeader1);
bRemoved2 = !bRemoved2;
}

The idea behind this test was to just see what would happen. Will it
even work, or will it throw an exception somewhere? (what's happening,
of course, is that the items are getting disposed multiple times and
added back to the respective collections even after being disposed.)

In the case of the list view, this actually seems to work. The column
header appears and reappears in the list view on successive clicks of
the button. The listView itself, however, does not. Clicking button1
the first time removes the listView, and clicking it a second time does
NOT put the listView back on my form. Still no exception on subsequent
calls to Dispose() though.

- Should these types of objects be treated semantically different? If
so, how do other objects work regarding Dispose()?
- Am I just getting lucky with the columnHeader case, but it may
exhibit other types of problems down the line?

I think the MSDN documentation leaves a bit to be desired here, because
all it says is something along the lines of "You need to call Dispose()
manually on these objects if they are not in the container when the
container itself is Disposed(). It doesn't answer the question of when
to Dispose() them though, and if they are reusable
 
M

Mehdi

- Should these types of objects be treated semantically different? If
so, how do other objects work regarding Dispose()?

No. All object from IDisposable classes should be treated in the same way:

- you should call Dispose whenever you do not need the object anymore. Note
that if the object is a control placed in a container control, the
container control will dipose it when disposed itself.

- if the IDisposable class has been implemented following Microsoft's
recommendations (you can safely assume that all the .NET framework classes
follow these recommendations), it is safe to call Dispose() on an object
that has already been disposed (that is: it is safe to call Dispose()
multiple times and this will not result in an exception to be thrown). In
other words, you can call Dispose() on your controls without having to
worry on whether they were still placed in their container control or had
been removed from it.

- You must never re-use an object that has been disposed. In the case of
Controls, you should not attempt to re-add a disposed control to a
container control or access its methods or properties. What happens when
you try to re-use a disposed object varies depending on how the developer
of the class has implemented it. He might have added explicit checks at
each of the properties and methods of the class to check if the object has
already been disposed and throw ObjectDisposedException is the object
happens to have been diposed. In this case, you won't be able to do
anything with the object anymore after it's been disposed. However, in many
cases, the developer doesn't bother adding these checks. If this is the
case, the object might appear to be still usable after having been disposed
but its behaviour will be unpredictible since it's internal state might now
be corrupted. Note that some class implement IDisposable only because they
derive from a class that implements it but do not actually do anything in
their Dispose method. In this case, re-using the disposed object would work
but since you are not supposed to know how the class has been implemented,
you should not rely on that. I'm not sure of what happens when a Control is
diposed. I suppose that, at the very least, its window handle is destroyed.
If you want to know what happens under the hood, have a look at Reflector.
- Am I just getting lucky with the columnHeader case, but it may
exhibit other types of problems down the line?

You might be lucky for the moment and encounter weird problems later or it
might be that the Dispose method of your columnHeader object does not
actually do anything. You can check that out using Reflector. In either
case, you should not attempt to re-use your columnHeader after having
disposed it as even if its implementation allows that for the moment,
nothing tells you that it's still going to be the case in a future version
of the .NET Framework.
 
Z

Zachary Turner

Thanks for the excellent answer. On that note, is it safe to assume
that it is perfectly valid and correct (despite the questionable
usefulness) to have the following code?

void f()
{
listView1.Columns.Add(Header1);
listView1.Columns.Remove(Header1);
listView1.Columns.Add(Header1);
}
 
M

Mehdi

Thanks for the excellent answer. On that note, is it safe to assume
that it is perfectly valid and correct (despite the questionable
usefulness) to have the following code?

void f()
{
listView1.Columns.Add(Header1);
listView1.Columns.Remove(Header1);
listView1.Columns.Add(Header1);
}

Yes, it is perfectly valid.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top