tags:

views:

3499

answers:

4
+1  Q: 

C# Dispose method

I'm having difficulty with C#'s 'Dispose' pattern. I have 3 classes at play here: A management class, a form, and a data-storage class.

The management class may (if it needs to) use the form to prompt the user for input. The form loads data from a file, which the user can then modify. As it is closed, the form must save this data back. The data-storage class implements .Dispose(), which does just that - writes the changes to disk.

Since this data-storage class (StoredInfo) is a member of the form (MyForm), MyForm must then also implement .Dispose() so as to call StoredInfo.Dispose. This is what is giving me issues. My management class, in it's code does:

Form.Dispose();
Form = null;

And my form:

// As written by MSVS. The exception is OK - I just want this to get called.
#region IDisposable Members

public void IDisposable.Dispose()
{
    throw new NotImplementedException();
}

#endregion

...yet the Form.Dispose() method is never called. Stepping with the debugger, execution goes:

Connector.Dispose() // The management class
    Form.Dispose()
Form.Dispose(bool disposing) // (1) Some method designer wrote?
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
Connector.Dispose() // Back to the management class!
    Form = null;

We somehow never called .Dispose, but instead called .Dispose(bool). In C++, where arguments can have default values, I can see this, but in C#, I'm lost. My best guess is that my debugger is not showing me what is actually happening.

Now, looking at the class hierarchy, I see other classes that implement IDisposable - so there must be another Dispose() member somewhere. It's not virtual, so I'm not sure why I'm not getting compiler errors. I tried overriding the .Dispose(bool) method, since that is getting called, and is virtual, but with this:

protected override void Dispose(bool disposing)
{
    StoredHosts.Dispose();
}

I get "Type 'ConnectorForm' already defines a member called 'Dispose' with the same parameter types", which, yes, I suppose it does... in Designer's code. So that's not an option. So back to calling Dispose(). But how? I'm missing the sheer simplicity and power and determinism of C++ destructors at the moment.

+1  A: 

There's no rule saying you can't fiddle with what the designer's done, as long as you're mindful not to break it. Feel free to add to the designer's Dispose() method, or to remove it and write one in the main source file.

The designer isn't magic. It just writes normal C#.

mquander
Aside from the "do not modify the contents of this method with the code editor.", but that's on a different method. Will the Designer blow away my method when I edit with it?
Thanatos
However I find you need to be careful as the Designer may decide to overwrite your changes. If I end up changing something in the .designer file I will move it to the .cs file so when the Designer makes more changes it leaves what I have changed.
auujay
In my experience, the designer won't screw up the Dispose method; mostly the only thing that it really fiddles with is InitializeComponents and the control declarations associated with it.
mquander
@Thanatos: No, it won't
SLaks
And you can still modify InitializeComponents -- it won't explode. I sometimes change it to initialize controls without using their default constructor. However, if you add other code to it that isn't in its usual format, that code might get blown away by the designer.
mquander
A: 

The Form class (or, to be precise, the Component class) defines its own Dispose method, which calls the virtual method Dispose(bool disposing)

You need to move the designer-generated Dispose method (which overrides the virtual method and is called by Component.Dispose) out of the Designer file, and then put StoredHosts.Dispose(); inside of it.

SLaks
A: 

by writing void IDisposable.Dispose() you effectively tells the runtime to call that particular version of the Dispose method if and only if the variable is of type IDisposable.

E.g.

Form f1 = new YourForm();
IDispoable f2 = new YourForm();

f1.Dispose(); //call to public void Dispose()
f2.Dispose(); //call to void IDisposable.Dispose()
Rune FS
+3  A: 

The Windows Form wizard puts special "region directives" around code that you're not supposed to modify, so you can modify the Dispose stuff as much as you like, as long as you stay within the pattern.

Think of IDisposable as the .NET way of doing destructors. As long as all implementers get it right, then the result can be equivalent to C++ destructors (in fact C++/CLI generates the Dispose method from destructor declarations and I deeply miss that feature in C#).

Read this for some background: http://msdn.microsoft.com/en-us/magazine/cc163392.aspx

There are a couple of things to be aware of. Firstly, the disposing parameter tells you wat context the Dispose(bool) virtual method is being called in. If it is false, then DO NOT DO ANYTHING! It means that you're being called from the finalizer thread. This is almost never useful and it's a historical flaw in the design of this pattern, because it has something 99.99% useful (deterministic destruction logic) mixed in with something only 0.01% useful (custom free-threaded finalization of native resources) as if they were best buddies.

So put your own cleanup code inside the if (disposing) branch.

Secondly, note how the wizard-generated code checks whether the object reference is non-null before calling Dispose on it. According to the definition of IDisposable, you should expect Dispose to be called multiple times for no reason on the same instance.

So you want to do this:

if (Form != null)
{
    Form.Dispose();
    Form = null;
}
Daniel Earwicker
Great! I used just that to dispose my non-designer created objects. However I moved the method out of the .Designer.cs file in order to better realize it in case the designer messes this up.
Marc