Dispose and the Designer
Recently, one of the tasks I had to do on my current work project was to reduce memory leaks. Even in C#, you have to worry about that. The garbage collector handles a lot of the common ones, but there are still the occasional one that needs to be removed to allow the collector to do its job.
For the most part, this involves running a memory profiler such as SciTech's .NET Memory Profiler. When I look for leaks, I usually do the following:
- Start up the profiler
- Perform an initialization step to make sure all the caches are loaded
- Get back to the "start page" of our application
- Take a memory snapshot
- Perform some action
- Get back to the "start page"
- Take another memory snapshot
- Figure out what should have been released and figure out why.
- Die a little inside
- Go back to 1
(Actually, we are all dying inside, but it is good to schedule internal death.)
One could say that it is pretty tedious. Now, I'm not the greatest hacker when it comes to handling memory programs, but we use the IDisposable
pattern pretty heavily, so I do a lot of the link breaking (e.g., nulling out fields, removing events) during the disposal process.
Somewhere in hour three of this, I realized I was struggling with the automatically created Dispose(bool)
method and had a minor idea of how to make it easier to work with.
Now, in C# with Visual Studio 2010 and the component designer, you get this nifty little block of code:
/// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); }base.Dispose(disposing);
}
There are two places this method is called: the finalizer and the Dispose()
method.
public ~SomeClass() { Disposing(false); }public void Dispose() { Dispose(true); }
From my understanding, the reason the flag is important is because a class is not suppose to go outside of itself during the finalize. This is because the finalization is indeterminate in order, you have no clue when it will run, and that class you are trying to disconnect may not exist.
There are a few frustrations with this. The first is that the Dispose(bool)
is put into the Designer.cs
class. And, it follows the conventions of the designer file, which in 99% of the cases mean, doesn't conform to your project's coding standards. Also, it's boilerplate.
There are times, when you want to dispose of something properly. There are two options. One, you manually add it to the Dispose(bool)
method in the designer source file. This always feels wrong because the designer is generated by code. I don't want to touch it since Visual Studio reserves the right to blow it away as needed (I know it won't, but the designer file is hidden for a reason).
Another way is moving Dispose(bool)
out of the designer and into the main code. This means you need to reformat it (trivial in this method). It also feels wrong to me since it was boiler plate and generated, but now you are moving it out of the generated file into the non-generated class.
A third approach is to use a partial method. This seemed like a really good idea when it was implemented, but I wish they did it for the boilerplate Visual Studio generates. To do this, you'd have this in the designer file.
protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); }InternalDispose(disposing); base.Dispose(disposing);
}
partial void InternalDispose(bool disposing);
Partial methods can't have accessors (private, public, internal) and they are always void return type. If the method doesn't exist in the other file, then it is never called (and compiled out). But, if you decide you need to override it, you just add this to the main C# file.
partial void InternalDispose(bool disposing) { // Do amazing things! }
Now the name, InternalDispose()
, is a poor one, but I don't have a really good name for it. I just want an automated hook so I don't have to mess to automatically generated boilerplate but still get the functionality I need, when I need it.
Metadata
Categories:
Tags: