1

I come from a C++ background and have trouble understanding the point of IDisposable objects (and the point of many other things in .NET). Why is a Dispose function necessary in the first place? Whatever it does, why not do that in the destructor of the class? I understand it cleans up managed resources, but isn't that what a destructor is supposed to do? I understand that

Using ( var obj = new SomeIDisposableObject ) 
{
    // ...
} 

is the equivalent of

var obj = new SomeIDisposableObject;
// ...
obj.Dispose();

but how does that save any typing? And if C# has a garbage collector, which it does, then why are we ever worried about disposing resources?

Is IDisposable/Using/etc. a Skeet-approved concept? What does he think of it?

user5648283
  • 5,913
  • 4
  • 22
  • 32

2 Answers2

8

IDisposable is nothing special. It's just an interface that makes you have a Dispose() function. IDisposable won't clear anything or destroy objects. A call to Dispose() does nothing if that function does nothing.

The use of IDisposable is a pattern. It's so important that it gets its own language construct (the using block), but it's just a pattern.

The difference with a destructor, is that in .NET, the destructor is non-deterministic. You never know when the garbage collector will collect your object. You don't even know if it even will (unlike using delete in C++, which is deterministic).

So IDisposable is there for deterministically releasing unneeded references (and releasing unmanaged resources). The "disposed" object will remain in memory after you call Dispose until the garbage collector decides to collect it (at which point, the "destructor" will be called, unless you explicitly tell it not-to), if it ever decides to.

That's the main difference to using "destructors" (or finalizers, as they are called in C#).

As for the why do we need it:

  1. Managed references to other objects prevent objects to be collected by the garbage collector. So you need to "release" those references (by, for example, setting the variables that reference those objects to null).
  2. Objects in .NET can use unmanaged resources, if they use native libraries. For example, every control or bitmap in a Windows.Forms application uses a Win32 handler, which is unmanaged. If you don't release those resources, you may reach the handle limit in Windows if using any big application, easily. Also, if you need interoperability with any non-.NET API, you'll more likely have to allocate unmanaged memory (with the use of, for example, Marshal.AllocHGlobal), which will need to be released at some point: that point is generally the Dispose() function of the IDisposable, which must be called explicitly (or by using a using block).

As for the using block, it's more equivalent to:

var myObject = new MyDisposableClass();
try
{
  ...
} 
finally {
  myObject.Dispose();
}

So it does indeed save typing

Jcl
  • 27,696
  • 5
  • 61
  • 92
1

Using a using block doesn't only call the .Dispose method; it calls the .Dispose method when you leave the block, however you leave it. So, if your code crashes, it will still call Dispose. The actual code would be closer to:

try
{
    var obj = new SomeIDisposableObject;
    // ...
}
catch (exception ex)
{
}
finally
{
    obj.Dispose();
}

Additionally, destructors don't always fire when you expect them to. I've had a few bugs where the destructor is called after the program has apparently exited, and tried to access resources that no longer exist. Since you don't know when it will be called, it's difficult to fix this.

Paul Michaels
  • 16,185
  • 43
  • 146
  • 269