0

I've been trying to increase my understanding of garbage collection, managed & unmanaged resources, and "proper design principles" regarding memory management as I am interested in getting into "lower level" programming and things of that nature.

I understand that you're supposed to use using block or some other solution for ensuring these unmanaged resources do in fact get disposed, but I don't understand what is happening under the hood.

I was looking at this article: Implementing a Dispose Method on MSDN and was confused by this particular line:

To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

Lets look at the example code they provide for the dispose pattern:

class DerivedClass : BaseClass
{
   // Flag: Has Dispose already been called?
   bool disposed = false;

   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) 
      {
         handle.Dispose();
         // Free any other managed objects here.
      }

      // Free any unmanaged objects here.

      disposed = true;

      // Call base class implementation.
      base.Dispose(disposing);
   }
}

I believe what the quoted text above is basically saying is, "We added a bool disposed property so that we can check if that's true and return if so. If it's false then we dispose of the actual resources. That way we don't actually dispose of something multiple times"

But this didn't make sense to me. If we've gotten rid of an object, how can you call Dispose on it a second time?

To investigate, I wrote a console application containing the following 3 lines:

var cmd = new SqlCommand();
cmd.Dispose();
cmd.Dispose();

This compiles and executes without problem -- which makes sense, given the quoted text from the article. But I don't understand what's actually happening.

I set a breakpoint and stepped over each line. After the first Dispose is called, I expected the Locals window in Visual Studio to tell me that cmd is null. And following this train of thought I asked myself, "How can you call Dispose on null?" Obviously, you can't. So what is happening? Why is cmd still a SqlCommand object after its been disposed of the first time?

enter image description here

What exactly does Dispose do, and if I've disposed of my object why does it seem to still exist for all intents and purposes?

Why / How does the following compile and run without problem?

var cmd = new SqlCommand();
cmd.Dispose();
cmd.CommandText = "I figured this would throw an exception but it doesn't";
sab669
  • 3,984
  • 8
  • 38
  • 75
  • 1
    There is no "under the hood". No magic involved, it's just a method like any other. The guy who writes the `Dispose` method must implement it right - calling all the appropriate `CloseHandle` functions and such. Can you change "this reference" to `null` in any other method? Of course not. Why would you expect `Dispose` could do that? It's just a method. – Luaan Dec 14 '16 at 15:20
  • 1
    Aside from Luaan's explanation, see also http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface for the bigger picture. – Cody Gray - on strike Dec 14 '16 at 15:24
  • 1
    You did pick about the worst possible example, [it does nothing useful](https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,1101). Probably did something very different back in .NET 1.0. But you are certainly not supposed to continue using an object after you disposed it, hopefully that is somewhat obvious. Might throw an ObjectDisposedException. Or it might not since the member doesn't do anything "heavy". Intentionally getting it wrong isn't all that useful. – Hans Passant Dec 14 '16 at 15:25
  • @HansPassant As you said, obviously you're not *supposed* to continue using an object after disposing of it, but I wanted to see what would happen if I tried while reading. And in any `Dispose` override I've seen they all pretty much look the same (ie. 'does nothing useful') so I didn't look too hard this time around for other things to Dispose of, as I clearly didn't / don't understand it. SqlCommand was just the first thing to pop into my head... But @CodyGray's link is tremendously helpful along with K Berger's answer. – sab669 Dec 14 '16 at 15:50

2 Answers2

2

The IDisposableinterface is being called by several consuming classes and - as already stated by Dark Falcon - when you encapsulate the usage of your class in a using block. This makes it easier to keep your unmanaged resources clean. But Disposeis just like any other method and not to be confused with destructors/finalizers (which is what you apparently expected it to be)

K. Berger
  • 361
  • 1
  • 9
  • "... and not to be confused with destructors/finalizers" ~I think this is the issue here. Reading about [`Object.Finalize`](https://msdn.microsoft.com/en-us/library/system.object.finalize(v=vs.110).aspx) now. I guess I figured that `Dispose` (through its series of `base.Dispose()` calls) ultimately called `Finalize` or did *something* other than whatever I might explicitly define in my `Dispose` override. So `Dispose` is literally just part of the syntactical-sugar that is the `using` statement and nothing more? – sab669 Dec 14 '16 at 15:21
  • Yes. There you go! :-) – K. Berger Dec 14 '16 at 15:23
  • 1
    @sab669 In practice, it's actually usually the other way around - the finalizer (which is not user callable at all) often calls a `Dispose` method, just to keep all the clean-up logic in one place. Finalizer in .NET is the final safety net to ensure unmanaged resources are disposed of even if something went wrong (and you forgot to call the proper `Dispose` method). Again, they are abused for other things, but this is the only thing they are designed for, and you lose a lot of guarantees in a finalizer, so writing a safe finalizer isn't entirely trivial. Even `this` can be `null` there :) – Luaan Dec 14 '16 at 15:29
1

There is nothing special about Dispose except for the fact that a using statement can call it for you. Other than that, it is just like any other .NET function. It does not magically set any variables to null, cause exceptions to be thrown if the object is used again, or anything like that.

Likewise, IDisposable is not a special interface in any way except that objects in a using statement must implement it. You could consider a using statement to be something like this:

// using(var a = somethingDisposable()) {otherCode();}
var a = somethingDisposable();
try
{
    otherCode();
}
finally
{
    if(a != null)
        ((IDisposable)a).Dispose();
}
Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • But... what does that mean though. What does Dispose do. Is it just incrementing the object's Generation so that *whenever* the Garbage Collector DOES run it will take care of those items? – sab669 Dec 14 '16 at 15:14
  • 1
    No, it has, as I said, absolutely zero interaction with the garbage collector. It is just another .NET method. Nothing special whatsoever except for the nice syntax of the `using` statement. – Dark Falcon Dec 14 '16 at 15:15
  • 1
    `Dispose`does, what you tell it to do. It's just a method. You usually use it to get rid of unmanaged resources in a controlled way (i.e. when working with your instances inside of a using-block). – K. Berger Dec 14 '16 at 15:15
  • 1
    @sab669 In particular, `Dispose` is not a deterministic memory collection mechanism or anything. It's only real purpose is to ensure deterministic disposal of *unmanaged* resources (though it's certainly abused for many other uses) - the ones that aren't under .NET's control. It's just a method like any other. Many classes also have methods like `Close` or `Destroy` or such that do the exact same thing (often through calling `Dispose` or vice versa). If you implement your class with an empty `Dispose` method, it will do *nothing*. – Luaan Dec 14 '16 at 15:18
  • @DarkFalcon As I responded to K. Berger's answer, I think the issue is that I was under the impression that `Dispose` "worked with" `Finalize` in *some* way. As I've only ever seen `Dispose` implemented in a manner that calls `base.Dispose()` I figured somewhere along the chain of inheritance it was doing **something**. – sab669 Dec 14 '16 at 15:24
  • There's nothing special about `IDisposable` in C# or VB.NET, other than the `using` support, but the C++/CLI language treats `Dispose` in special fashion, in some cases automatically generating a `Dispose` function that calls `Dispose` on fields that are declared as requiring cleanup; inheritance classes that implement `IDisposable` may be problematic in C++/CLI if those classes don't use the normal dispose pattern. – supercat Dec 14 '16 at 17:40