68

I've read about disposing objects/IDisposable interface and destructors in C#, but to me they seem to do the same thing?

What is the difference between the two? Why would I use one over the other? In fact, in this example (link below) this code uses both the IDisposable interface and a destructor:

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

The comment says the destructor is if the finalization code is not used, but how do I decide when to use one over the other?

GurdeepS
  • 65,107
  • 109
  • 251
  • 387
  • 2
    See [this question](http://stackoverflow.com/questions/331786/since-net-has-a-garbage-collector-why-do-we-need-finalizers-destructors-dispose). – Sam Saffron Jan 19 '09 at 00:35
  • Possible duplicate of [What is the difference between using IDisposable vs a destructor in C#?](http://stackoverflow.com/questions/339063/what-is-the-difference-between-using-idisposable-vs-a-destructor-in-c) – user Feb 24 '16 at 17:20

3 Answers3

85

I wrote a fairly in-depth post which should help to explain about finalizers, IDisposable, and when you should use one or the other: http://gregbee.ch/blog/implementing-and-using-the-idisposable-interface

Probably the most relevant part is quoted below:

When you are using unmanaged resources such as handles and database connections, you should ensure that they are held for the minimum amount of time, using the principle of acquire late and release early. In C++ releasing the resources is typically done in the destructor, which is deterministically run at the point where the object is deleted. The .NET runtime, however, uses a garbage collector (GC) to clean up and reclaim the memory used by objects that are no longer reachable; as this runs on a periodic basis it means that the point at which your object is cleaned up is nondeterministic. The consequence of this is that destructors do not exist for managed objects as there is no deterministic place to run them.

Instead of destructors, C# has finalizers which are implemented by overriding the Finalize method defined on the base Object class (though C# somewhat confusingly uses the C++ destructor syntax ~Object for this). If an object overrides the Finalize method then rather than being collected by the GC when it is out of scope, the GC places it on a finalizer queue. In the next GC cycle all finalizers on the queue are run (on a single thread in the current implementation) and the memory from the finalized objects reclaimed. It's fairly obvious from this why you don't want to do clean up in a finalizer: it takes two GC cycles to collect the object instead of one and there is a single thread where all finalizers are run while every other thread is suspended, so it's going to hurt performance.

So if you don't have destructors, and you don't want to leave the cleanup to the finalizer, then the only option is to manually, deterministically, clean up the object. Enter the IDisposable interface which provides a standard for supporting this functionality and defines a single method, Dispose, where you put in the cleanup logic for the object. When used within a finally block, this interface provides equivalent functionality to destructors. The reason for finally blocks in code is primarily to support the IDisposable interface; this is why C++ uses simply try/except as there is no need for a finally block with destructors.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • 3
    Link is dead apparently, do you have another link for the post? – Sune Rievers Jan 20 '11 at 14:52
  • 1
    At least right now you can still retrieve it from Google's cache: http://goo.gl/1dTmL – Gorgsenegger Apr 09 '12 at 15:46
  • 8
    Slight nit: the syntactic element in C# which is notated with a tilde followed by the class name is properly called a destructor in C#, because that is the term used by the people who wrote the C# spec. The behavior of C# destructor bears no relation to that of a C++ destructor, and the name is rather silly since a C# destructor doesn't actually destroy an object but rather gives an object *which would otherwise be destroyed* a reprieve so it can put its affairs in order. – supercat Apr 27 '12 at 22:24
  • @supercat - Microsoft have actually used both words to describe this functionality in the context of C# in the past. Although the official documentation tends to use the word "destructor" I prefer to use the term "finalizer" to avoid confusion or implying the wrong semantics. Many of the people who designed the .NET framework agree with this stance: http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae – Greg Beech Apr 30 '12 at 09:45
  • @GregBeech: The term "finalizer" refers to an override of `Object.Finalize`, regardless of how such override is produced. The term "destructor" refers to the syntactic element which directs the C# compiler to produce a finalizer which calls the specified code as well as the base class' finalizer. – supercat Apr 30 '12 at 13:32
  • @supercat - As I said, yes technically that is true, but I still don't like to use the term, even in the context of C#. Read the linked resource and you will see the comment: *"C# arguably emphasized the wrong aspect of object lifetime (instead of resource lifetime) by giving the special C++ name the wrong meaning. But note that when you see the word destructor or ~T() in any discussion on Dispose() or object lifetime, pay attention to exactly what the author intended. I prefer using “finalizer” and “dispose” to alleviate any language-induced confusion." - Brian Grunkemeyer* – Greg Beech Apr 30 '12 at 16:19
  • 1
    @GregBeech: C# *definitely* emphasized the wrong aspect of object lifetime, and did so in a fashion which pretends to be portable but really isn't. Further, from the CLR perspective, the GC runs finalizers and has no concept of what a destructor is. I think we're in violent agreement there. My point was that in discussing the quirks of how C# wraps finalizers, it is necessary to use the term "destructor", no matter how unfortunate that choice of terminology might be. – supercat Apr 30 '12 at 16:34
24

Short version

A finalizer gives you an opportunity to dispose of unmanaged resources in case the user of your object forgot to call IDisposable.Dispose.

If your object implements IDisposable, the user of your object must call .Dispose. You don't have to clean up the user's mess; but it's a nice thing to do.


My most popular answer on Stackoverflow walks you from the beginning why you have IDisposable, what it should do, what your finalizer can do, what it shouldn't do.

This answer melts faces

has been used to describe it :P

Community
  • 1
  • 1
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • 1
    YOU CAN DO BETTER! (sorry, had to. also, couldn't resist an upvote for that answer, though a final 'this is what a basic class should look like' at the end would help others probably) On a side note, that's a community wiki answer, so you don't get points for that, right? – JoeBrockhaus Jan 07 '15 at 17:27
  • 1
    @JoeBrockhaus I think i don't. That's what happens if you keep editing an answer to make it better. – Ian Boyd Jan 08 '15 at 14:32
5

Having destructor (~Object()) in managed programming language is the most dummest idea. It perfectly make sense for unmanaged languages like C,C++ to have destructors as they use RAII idiom but for managed like Java,C#, just so absurd.

It has been pointed out by Joshua Bloch, a former project lead in Java Collection Framework, that the idea of finalize() method (which is equivalent to C#'s C++ like destructor) in Java was the biggest mistake ever made. Same as C#, finallize() in Java gives overhead to "new" as it must be added to the finallizer queue during allocation. More over, Garbage Collector must pop and run finallize() in the queue, so twice the overhead during gc.

C# had many enhanced features like "using(IDisposable) {}" which not only allow the IDisposable variable to be confined to the scope of "using" block but also guarantee it's cleanup. My question is, why did C# follow the same trail of Java which lead to great mistake. May be if the development of dotnet started after around 2003 ~ 2005, when many Java architects found the fallacy of finallize(), then the mistake would have been prevented.

Many good idea of one language is often transferred to other language like the "IDisposable/using combo " in C# which was transferred to Java 1.7 in its "try(object-to-dispose) {}" statement. But its too bad that language architects fail to discover the bad idea disguised as good idea during it's transfer from one to the other.

My advise is never to use ~Destructor() and stick with IDisposable/using combo if you need to manually cleanup the unmanaged resource like database connections.

David Lee
  • 859
  • 1
  • 11
  • 13
  • Hi David, IDisposable was necessary to manage allocated memory not allocated by the framework like windows used in WinForms. It is necessary until the completion of langage (no external dependencies). – Eric Ouellet Jan 07 '13 at 19:52
  • 1
    I agree in a theory sense; it would be better not to need them. However, since you can't count on your caller to call dispose in practice, you sometimes need the extra trapdoor to ensure finalization. The disposable pattern (http://goo.gl/YoGsr) is specifically to cause finalize to be called only if the client neglects to call dispose. Adhering to a "because theory!" argument gets us to having mandatory using statements for anything IDisposable, which is not a tenable solution -- even worse than the checked/unchecked exception issues in java. – danwyand Jul 16 '13 at 16:57
  • Hi @dcw, I admit that having both Dispose() and finallize() makes it a bullet proof solution for managing resource that has to be cleanup before being garbage collected. But I hardly see a case where finallize() actually comes to the rescue and cleans up the mess like garbage collection pause or DB pool empty error due to not calling Dispose(). I saw it happen in both Windows 2003/asp.net and IBM AIX/JDK1.5/spring. Mostly in these case, webapp is paused indefinitely and I just consider the gabage collection thread being dead so I just restart the specific COM+ or Java web container. – David Lee Jul 17 '13 at 09:38