tags:

views:

163

answers:

6

hello

i've read msdn and various posts about dispose pattern, and there are still a couple of things i don't understand. I've written the following code to test the dispose pattern. Please note that there aren't unmanged resources, i'm using vs2008 and .net 3.5 :

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void tryDispose()
    {
        //test 1 : allocate resource and leave manage it to gc
        BL.myclass df = new BL.myclass();
        //test 2 : try to force garbage collecting
        //GC.Collect();
       //test 3 : call dispose myself
       //using (BL.myclass df = new BL.myclass())
        //{ 

        //}

    }

    private void button1_Click(object sender, EventArgs e)
    {
        tryDispose();
    }

this is my disposable class:

class myclass: IDisposable
{

    private StronglyTypedDs myDS;
    private bool _disposed;

    public myclass()
    {
        using (myDSTableAdapter docDocadpt = new myDSTableAdapter())
        {
            myDS = new StronglyTypedDs();
            docDocadpt.Fill(myDS.TheTable);
        }
    }


    #region IDisposable Members

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

    ~myclass()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                if (myDS != null)
                    myDS .Dispose();
                myDS = null;
            }
        }
        _disposed = true;
    }


    #endregion

}

The results are :

test 1a - just instantiating myclass, the destructor is commentend since myclass doesn't contains unmanaged resources : myclass.dispose is not called, even if i close the application (whose dispose is executed instead) . So what's the state of the dataset once i close the application ?

test 1b - uncommenting destructor, it's ok all disposes are called when i close the application.

test 2a and 2b - i do the above test just calling gc.collect : the behaviour is identical to test 1a and 1b respectively

test 3 - everything works fine (of course)

many posts says that, if myclass doesn't contains unmanaged resources, i don't need to add the destructor; yet in my tests , if i don't add the destructor , myclass.dispose doesn't get called when i close the application. I haven't waited for the gc to run by itself (if i remember correctly gc.collect doesn't guarantes the class instance is deallocated) to check if it will call myclass.dispose . So what's the correct implemention : always define e destructor or avoid it if myclass contains only managed resources ? If i had filled all generations levels, would had the gc called myclass dispose or not without having implemented a destructor?

Finally i've noticed that if i define a destructor , but don't declare the class as implementing IDisposable, the disposing chain works anyway. This could make sense since the destructor might be translated to finalize in IL. But i find it really confusing : is it some kind of "implicit" interface implementation i don't know? gc can dispose the item but users can't

thank you in advance

Stefano

+1  A: 

The correct pattern is to only use a finalizer when your class contains unmanaged resources. As to relying on the GC to dispose of your managed objects, don't. The IDisposable contract makes it clear that this object needs to be disposed.

ChaosPandion
Additionally, if the object is not Disposed, but does not have any unmanaged resources, then this should not be fatal, in principle. Any managed classes it referenced should eventually be collected and if those classes had unmanaged resources, they should be cleaned up when those classes are finalized. That said, it's _much_ better to call Dispose explicitly if the class is IDisposable, as you could leave unmanaged resources hanging around for an indeterminate period of time until the finalizer eventually gets around to freeing them.
Dan Bryant
@Dan - Agreed, it may not be fatal, but it is certainly a good idea.
ChaosPandion
@Dan - If one is using RAII, then calling `Dispose` is required for correctness. One famous example is the stream writers. If `Dispose` is skipped for a RAII class, then the *resources* will be cleaned up just fine, but the results may be wrong.
Stephen Cleary
@Stephen, that's a good point; Disposal isn't always purely about freeing resources.
Dan Bryant
A: 

The main purpose of IDisposable is to have a consistent standard interface you can dispose of unmanaged resources with, that is if you didn't do something to ensure Dispose() was called these resources would hang around after the app was closed. Also understood by the using() syntax, that is using will implement the following block for you:

DisposableType someDisposable = new DisposableType();
try
{
    // Do whatever
}
finally
{
    ((IDisposable)someDisposable).Dispose();
}

This is all implemented in a nice design like so:

using(DisposableType someDisposable = new DisposableType())
{
    // Do whatever
}
Jimmy Hoffa
+8  A: 

Trust your Garbage Collector. Managed resources will get cleaned up, eventually. There is no need for a finalizer or implementing IDisposable unless you have some external resource that needs to be released.

This typically means that you only should implement IDisposable when you either:

  1. Are wrapping a native resource. (In this case, you probably also want a finalizer.)
  2. Are encapsulating a class which implements IDisposable. (In this case, you want IDisposable, but don't need to implement a finalizer/destructor.)

Part of the confusion with IDisposable, in my opinion, is that it covers quite a few use cases, and the proper implementation changes depending on what you're doing (ie: wrapping native resources, wrapping other IDisposable classes, or using Factored types). To address this, I've written a multiple part series on IDisposable - it might help clarify some of this for you.

Reed Copsey
+1, but I have one clarification on item #1. I'd change it to "You are wrapping an _original_ native resource." ie: you don't need a finalizer for a class that wraps something like SqlConnection, even though SqlConnection embodies a native resource, because you aren't the original wrapper for the resource. SqlConnection already has you covered.
Joel Coehoorn
@Joel: That's number 2 (encapsulating a class which implements IDisposable) ;)
Reed Copsey
Joel Coehoorn
A: 

Ok i think i've understood, referring to my example its correct to implement a dispose because the dataset is global to my class and implements IDisposable , while i don't need the finalizer because there aren't unmanaged resources. Even if i "forget" to dispose some managed resource in my class dispose method, the gc will collect it at some point. The dispose method is just an utility i provide to other classes/developers for managed resources, a must with the finalizer if i wrap unmanaged resources .

i'll read the articles you have provided asap, but in the mean time i've the last question : when gc will free memory owned by my class and its resources ? when someone calls dispose or when it will run (it will free memory instead of moving it to next generation ) ?

thank you everybody for your answers and examples

Stefano
+1  A: 

Your code is correct, you've implemented it exactly like it is documented in the MSDN library.

You'll need to take a second look though. Reason what happens when the destructor (aka finalizer) runs. The disposing argument will be false, the protected Dispose method does nothing. This is entirely normal, finalizers should only ever release unmanaged resources. You don't have any. It is extraordinary rare to ever have an unmanaged resource in your own code. They belong in the nice wrapper classes available in .NET to turn an unmanaged operating resource into a nice managed class. If you find yourself thinking you need a finalizer, you'll be wrong 99.99% of the time. Even if you do wrap an unmanaged resource, you should use one of the SafeHandle wrappers. And rely on their finalizers.

Okay, you want to get rid of the destructor. It isn't healthy to leave it in, it keeps the object in memory longer than necessary. When you do, you'll cut it down to:

public void Dispose()
{
    if (myDS != null) myDS.Dispose();
}

Which is the boiler-plate implementation of most any Dispose() method, just call the Dispose method of members of the class. You can completely omit it if you don't have any members with a Dispose() method.

Next, you do misunderstand how the Dispose() method gets called. It is not automatic. Which is the point of having it in the first place, releasing resources automatically is already taken care of by the garbage collector. The Dispose() method is there for you to call, either with the using statement or calling it directly. So that you can release the resource early instead of waiting for the garbage collector finalizer thread to get around to it. Which can take a while. Call it when you know that your program won't be using the object anymore.

If your DataSet is actively used by the form then you cannot dispose it until the form closes. Call the class' Dispose() method in a FormClosed event handler. Or, better, open the form's Designer.cs file, cut-and-paste the Dispose() method you find in there and move it to the form's source code file. And add the dispose call. I know that's a bit confuzzling, but the only time it's okay to edit the designer file.

Hans Passant
+1  A: 

I wrote a brief seris entitled How to Implement IDisposable and Finalizers: 3 Easy Rules. It describes a much simpler approach that Microsoft themselves have followed since the 2.0 version of the BCL.

The "official" pattern is needlessly complex and needlessly confusing.

Stephen Cleary