views:

528

answers:

4

I'm working on a piece of library code around IDisposable. The managed path (via using) is easily testable. I'm wondering about the finalizer though: Is calling System.GC.Collect() sufficient to force the finalizer to run?

A: 

Could you mock out an IDisposable interface and expect a call to Dispose? That would at least let you see when the object is actually disposed.

brien
Setting a breakpoint is sufficient to discover that. I'm testing the Dispose()'s implementation though, not whether it's called.
David Schmitt
+5  A: 

No, the GC.Collect() call is asynchronous, you would also need to call this:

System.GC.WaitForPendingFinalizers();
RickL
Thanks a heap! I knew why I asked :-)
David Schmitt
I think the GC.Collect() itself is synchronous (i.e. it will have freed any memory it can by the time it returns) but the finalizers themselves run separately. I could be entirely wrong though...
Jon Skeet
I'm doing both now and the test passed every time. That is, called the correct extension point from the finalizer.
David Schmitt
Note to self: also check that the initialisation code of the class in question doesn't "root" the object somewhere.
David Schmitt
"Thanks a heap!" LOL, no pun intended...
Even Mien
A: 

I think I would lean towards making Finalize() call another method, and test that the other method does what you want. You wouldn't get 100% code coverage, but at least you'd know that the method releases the object's resources properly.

Ben Fulton
+1  A: 

I would take a look at Dispose, Finalization, and Resource Management its the best reference on the subject I know of. Using their pattern:

~ComplexCleanupBase()
{
    Dispose(false);
}

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

protected override void Dispose(bool disposing)
{
   if (!disposed)
   {
        if (disposing)
        {
            // dispose-only, i.e. non-finalizable logic
        }

        // new shared cleanup logic
        disposed = true;
    }

    base.Dispose(disposing);
}

You wind up with dead simple Finalizer/Dispose() methods and a testable Dispose(bool). No need to force Finalization or anything using the GC class.

Rob McCready
Yeah, I'm doing it quite like that. It's only a slight adaptation of what's recommended in the MSDN.
David Schmitt