The terms Finalizers, Destructors and Dispose methods are used interchangeably making developers ignore their proper usage. Managed environments can clean-up most of the RAM memory you use, except for unmanaged resources, so here is where Finalizers and Dispose methods come into play.

This native-managed impedance mismatch has led engineers think that all the “cleaning code” is created and called automatically. The fact that unmanaged resources are basic building blocks so it’s important to underline their differences and oversee their correct usage. Simply put, Finalizers are implicit resource clean up, which means that your write the Finalize method and the Garbage Collector will probably call it. Finalizers use destructors’ syntax, so we can not call them directly.

Finalizers work well within managed environments, but while working with unmanaged resources you need to clean up resources exactly when you finish using them to improve performance. Having into account that our applications use databases, files and other resources it’s important to know how to do it.

Unmanaged resources should be cleaned by calling their Dispose method. This method is not called automatically, so if you use Database connections you should close them and Dispose them manually. If you use command objects you should Dispose them as well, the same for DataReaders. You might surprise finding out how many objects need to be Dispose. Everywhere you see a Dispose method use it.

In my years leading and managing Software Development Teams with different sizes and seniority levels I have found most people forget about the Dispose method, so they spend hours trying to figure out why the database is crashing magically. I see the point of managed environments but if many people fail to follow a rule, maybe the rule is somewhat excessive.

I think the Dispose method is just a trick to pass by the garbage collector’s intelligence. I guess it should be handled automatically but we have to live within these constrains, so by the meanwhile we have to adhere to the Dispose method. Anyway I don’t agree that the Dispose method is pattern because for me it makes part of the object destruction. If we would accept it as a pattern, then the new keyword and the constructor overloading should be Design patterns too. Labelling the Dispose method as a Design pattern is a media circus.

From a Software Designer’s point of view, we should provide a standard way to clean-up resources; otherwise everyone will come up with a different handy solution. In order to avoid scattering in your projects, design a class to take care of objects’ Dispose. For example: if you use ADO.Net classes probably you have the Close and Dispose method sprayed out all around, so you can centralize them by using an Extension method like the following implementation.

public static void ForceDispose(this IDataReader dr)
        {
            if (dr != null)
            {
                dr.Close();
                dr.Dispose();
            }
        }

        public static void ForceDispose(this DbCommand command)
        {
            if (command != null)
            {
                if (command.Connection != null &&
                    command.Connection.State == ConnectionState.Open)
                {
                    command.Connection.Close();
                }
                command.Dispose();
            }
        }
    }

Note: although I think the ADO.Net API is simple and good, it has multiple design issues; writing statements like command.Connection.State (instead of command.GetConnectionState()) completely breaks encapsulation.

Your team could consume the class like the following code:

            command = db.GetStoredProcCommand("AnyCommandText");
            IDataReader dr = null;

            try
            {
                dr = db.ExecuteReader(command);
                ...
            }
            finally
            {
                dr.ForceDispose();
                command.ForceDispose();
            }

As a Software Designer you always have to consider many options. So the method is implemented as an Extension because the following advantages:

  • It allows us perform Null checking.
  • It allow us extend it to virtually any existing code.
  • It does not break encapsulation.
  • It is fluent.
  • Exception management support.

By implementing it with ref objects, partial classes or other mechanism you may be loosing any of the mentioned advantages. The official C# workaround it is the using statement, which I consider it a C# oddity because it force us enclosing in brackets (imagine the nesting levels you will reach) and it is executed until an exception is raised, so you will quickly start writing boilerplate code like the below implementation.

        private void f(int x)
        {
            //...
            using (StreamReader sr = GetStreamReader())
            {
                //...
            }
        }
        private StreamReader GetStreamReader()
        {
            StreamReader sr = null;
            try
            {
                sr = new StreamReader(this.filename);
            }
            catch (IOException ex)
            {
                //...
            }
            return sr;
        }

The Dispose method is implemented in modern libraries as well (like the Controller and the DbContext classes, to mention a few), so let’s give it the importance and treatment it deserves while focusing in software design and architecture.

[12/NOV/2012: Guidelines for using the Finalize and Dispose methods in MSDN]

Cheers,

Javier Andrés Cáceres Alvis

Microsoft Most Valuable Professional – MVP
Intel Black Belt Software Developer
Anuncios