Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding disposable objects

I've looked in SO about a question like this one, and even that I've found quite a few, any of those threw any light into this matter for me.

Let's assume I have this code:

public class SuperObject : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing) { }
}
  • Do I need the protected virtual void Dispose(bool) on SuperObject? Since there is really nothing to dispose there.
public interface ICustom : IDisposable { }
public class Custom : ICustom
{
    public SuperObject Super { get; protected set; }

    public Custom()
    {
        Super = new SuperObject();
    }

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

    public virtual void Dispose(bool disposing)
    {
        if (!disposing) return;

        if (Super != null)
            Super.Dispose();
    }
}
public class Foo
{
    public Foo()
    {
        using (var c = new Custom())
        {
            //do magic with c
        }
    }
}

Now what happens if I want/need/try to use Custom on a class like System.Web.Mvc.Controller which already implements and has implemented IDisposable?

public class Moo : Controller
{
    Custom c;

    public Moo()
    {
        c = new Custom();
    }

    // Use c throughout this class        
}

How to properly dispose c in Moo?

like image 948
Esteban Avatar asked Dec 15 '22 05:12

Esteban


2 Answers

The normal approach is to apply the standard IDisposable implementation - HOWEVER this is really only necessary if your class or some class that derives from it will use UNmanaged resources - this case is infact VERY rare (and when this case does apply it is better to wrap the unmanaged resource in its own class that has a full standard IDisposable implementation).

So assuming you are not dealing with UNmanaged resources (raw file handles, globally alloced memeory etc) and are only dealing with members that are disposable (i.e that have managed resources and implement IDisposable) then you can safely get a way with a mimimal implimentation of IDispose - that is:

Just have a single void Dispose() method. In that method just call dispose on dispoable members and then Dispose on the base class if its disposable. If you have a class hierachy its ok to make this Dispose virtual. There is no need to have a Dispose(bool) method. Nor is there any need to check if the object is disposed - because all your doing is calling dipsose on other objects and those implementation will do that check.

If you don't like the mimimal appraoch then apply the standard full implimentation (but it is not strictly necessary). I.e either do a standard implimentation because your a stickler for following the recommended approach OR do a simple minimal (but correct) implementation - but don't do something in between (i.e not standard, not simple or not correct)!

See this question for more details: Minimal IDispose implimenation for managed resources only

So in your case the following is the mimimal implimentation:

public class SuperObject : IDisposable {
    public void Dispose() {
        // Dispose code...just call dispose on dispoable members.
        // If there are none then no need to implement IDisposable!
    }
}

public interface ICustom : IDisposable { }
public class Custom : ICustom {
    public SuperObject Super { get; protected set; }

    public Custom() {
        Super = new SuperObject();
    }

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

public class Moo : Controller {
    Custom c;

    public Moo() {
        c = new Custom();
    }

    public Dispose() {
        if (c!=null)
            c.Dispose()
        base.Dispose();       
    }
}

Note that if Super object does not have any disposable resources then there is no point in implementing IDisposable and having a Dispose method. If Customs only disposable object is SuperObject then the same applies there, and again the same logic rocks through to Moo. Finally then if all the above applies and there are no other disposable objects around all you need really need is:

  public class Moo : Controller {
        Custom c;
    
        public Moo() {
            c = new Custom();
        }
    
        public Dispose() {
           base.Dispose();       
        }
    }
like image 85
Ricibob Avatar answered Dec 22 '22 00:12

Ricibob


How to properly dispose c in Moo?

public class Moo : Controller
{
    Custom c;

    public Moo()
    {
        c = new Custom();
    }

    // Use c throughout this class    


    protected override Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
           c.Dispose()
    }
}

And that also answers your first question, Controller needs to make its Dispose(bool) method protected virtual or the above would not be possible.

But a few notes:

  • you don't have any isDisposed logic. It is a good idea to do the disposing only once, and you may want to trap usage-after-dispose.
  • omitting destructors (finalizers) is in itself a good idea but you now have the extra constraint that no derived class should own an unmanaged resource.
like image 24
Henk Holterman Avatar answered Dec 21 '22 22:12

Henk Holterman