ENTERPRISE

Memory Management : Clean Up Managed Resources Using the Dispose Pattern

9/22/2012 9:17:53 PM
Scenario/Problem:You need to ensure that managed resources (such as database connections) get cleaned up when needed.

Many types of resources are limited (files, database connections, bitmaps, and so on) but have .NET classes to manage them. Like unmanaged resources, you still need to control their lifetimes, but because they are wrapped in managed code, finalizers are not appropriate.

Solution:You need to use the dispose pattern. Let’s look at a similar example as the last section, but with a managed resource. The IDisposable interface defines a Dispose method, but to use the pattern correctly you need to define a few more things yourself:
public class MyWrappedResource : IDisposable
{
    //our managed resource
    IDbConnection _conn = null;

    public MyWrappedResource(string filename)
    {
    }

    public void Close()
    {
        Dispose(true);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    private bool _disposed = false;
    protected void Dispose(bool disposing)
    {
        //in a class hierarchy, don't forget to call the base class!
        //base.Dispose(disposing);
        if (!_disposed)
        {
            _disposed = true;
            if (disposing)
            {
                //cleanup managed resources
                if (_conn!=null)
                {
                    _conn.Dispose();
                }
            }
            //cleanup unmanaged resources here, if any
        }
    }
}

					  

When using the dispose pattern, you call Dispose yourself when you’re done with the resource:

//use try-finally to guarantee cleanup
MyWrappedResource res = null;
try
{
    res = new MyWrappedResource("TestFile.txt");
}
finally
{
    if (res != null)
    {
        res.Dispose(true);
    }
}

This pattern is so common that there is a shortcut syntax:

using (MyWrappedResource res = new MyWrappedResource("TestFile.txt"))
{
    //do something with res
}

Note

You should not use the Dispose pattern with Windows Communication Framework objects. Unfortunately, they are not implemented according to this pattern. You can find more information on this problem by doing an Internet search for the numerous articles out there detailing this problem.


Use Dispose with Finalization

Whereas finalizers should not touch managed objects, Dispose can (and should) clean up unmanaged resources. Here’s an example:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Data;

namespace Dispose
{
    public class MyWrappedResource : IDisposable
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto,
                CallingConvention = CallingConvention.StdCall,
                SetLastError = true)]
        public static extern IntPtr CreateFile(
              string lpFileName,
              uint dwDesiredAccess,
              uint dwShareMode,
              IntPtr SecurityAttributes,
              uint dwCreationDisposition,
              uint dwFlagsAndAttributes,
              IntPtr hTemplateFile
              );

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        //IntPtr is used to represent OS handles
        IntPtr _handle = IntPtr.Zero;

        //our managed resource
        IDbConnection _conn = null;

        public MyWrappedResource(string filename)
        {
            _handle = CreateFile(filename,
                0x80000000, //access read-only
                1,          //share-read
                IntPtr.Zero,
                3,          //open existing
                0,
                IntPtr.Zero);
        }

        ~MyWrappedResource()
        {
            //note: in real apps, don't put anything in
            //finalizers that doesn't need to be there
            Console.WriteLine("In Finalizer");
            Dispose(false);
        }

        public void Close()
        {
            Dispose(true);
        }

        public void Dispose()
        {
            Dispose(true);
        }
        private bool _disposed = false;
        protected void Dispose(bool disposing)
        {
            //in a class hierarchy, don't forget
            //to call the base class!
            //base.Dispose(disposing);

            Console.WriteLine("Dispose({0})", disposing);
            if (!_disposed)
            {
                _disposed = true;
                if (disposing)
                {
                    //cleanup managed resources
                    if (_conn!=null)
                    {
                        _conn.Dispose();
                    }
                    GC.SuppressFinalize(this);
                }

                //cleanup unmanaged resources, if any
                if (_handle!=IntPtr.Zero)
                {
                    CloseHandle(_handle);
                }
            }
        }
    }
}

					  

Here’s a little program to demonstrate what happens:

static void Main(string[] args)
{
    using (MyWrappedResource res = new MyWrappedResource("TestFile.txt"))
    {
        Console.WriteLine("Using resource...");
    }

    MyWrappedResource res2 = new MyWrappedResource("TestFile.txt");
    Console.WriteLine("Created a new resource, exiting");
    //don't cleanup--let finalizer get it!
}

It gives the following output:

Using resource...
Dispose(True)
Created a new resource, exiting
In Finalizer
Dispose(False)
Other  
  •  Memory Management : Get the OS View of Your Application's Memory, Clean Up Unmanaged Resources Using Finalization
  •  Active Directory Domain Services 2008 : Create a WMI Filter, Import a WMI Filter, Export a WMI Filter
  •  Active Directory Domain Services 2008 : Filter Group Policy Object Scope by Using Security Groups, Disable User Settings in a Group Policy Object, Disable Computer Settings in a Group Policy Object
  •  Active Directory Domain Services 2008 : Block & Remove Block Inheritance of Group Policy Objects, Change the Order of Group Policy Object Links
  •  The Future Of Apple: Chip Off The Block (Part 10)
  •  The Future Of Apple: Chip Off The Block (Part 9)
  •  The Future Of Apple: Chip Off The Block (Part 8)
  •  The Future Of Apple: Chip Off The Block (Part 7)
  •  The Future Of Apple: Chip Off The Block (Part 6)
  •  The Future Of Apple: Chip Off The Block (Part 5)
  •  
    Most View
    Exchange Server 2010 : Outlook Integration (part 1) - Integration Overview
    Windows Server 2012 : Server Manager (part 2) - Configuring remote management
    Nikon AF-S Nikkor 80-400mm F/4.5-5.6G ED VR II Lens Review
    Spam Texts Will Finally Be Tackled As Networks Take Action
    HTC One - Great For Music-Lovers (Part 1)
    iBall Andi 4.5H - Pretty In White
    10 Essential Hardware Accessories (Part 2)
    Windows Phone 8 Group Test – June 2013 (Part 4)
    PNY NVidia GeForce GTX 660 Ti 2GB
    Microsoft Exchange Server 2010 : Managing Public Folder Settings (part 3) - Manipulating, Renaming, and Recovering Public Folders
    Top 10
    Mitsubishi Hybrids – One Direction
    Race To The Clouds – Honda R&D’S ’91 NSX (Part 2)
    Race To The Clouds – Honda R&D’S ’91 NSX (Part 1)
    Volkswagen Plug-In Hybrid Up – Double Act
    Pre/Power Amplifier Marantz SA8005/PM8005 Review (Part 2)
    Pre/Power Amplifier Marantz SA8005/PM8005 Review (Part 1)
    Smart TV Finlux 50FME242B-T Review (Part 2)
    Smart TV Finlux 50FME242B-T Review (Part 1)
    The Best Money Can Buy: Motherboards (Part 2) - Asus Rampage IV Black Edition, Asus Crosshair V Formula-Z
    The Best Money Can Buy: Motherboards (Part 1) - ASRock X79 Extreme 11, Asus Intel Z97 ROG Bundle, Gigabyte Z97X-GAMING G1