What’s the difference between a destructor and a finalizer?

What’s the difference between a destructor and a finalizer?

Rate This
  • Comments 20

Today, another dialogue, and another episode of my ongoing series "what's the difference?"

What’s the difference, if any, between a “destructor” and a “finalizer”?

Both are mechanisms for cleaning up a resource when it is no longer in use. When I was asked this, at first I didn’t think there was a difference. But some Wikipedia searches turned up a difference; the term “destructor” is typically used to mean a deterministically-invoked cleanup, whereas a “finalizer” runs when the garbage collector says to run it.

Doesn’t that mean that the C# spec uses the term “destructor” incorrectly?

Yes, by these definitions, the C# spec gets it wrong. What we call a “destructor” in the spec is actually a finalizer, and what we call the “Dispose()” method invoked by a “using” statement is in fact a “destructor”.

The CLI spec calls the finalizer by its right name.

Why did the authors of the C# spec get it wrong?

I don't know, but I can guess. I have two guesses.

Guess #1 is that on May 12th, 1999 there was not a Wikipedia article clearly describing the subtle difference between these two concepts. That's because there wasn't a Wikipedia. Remember back when there wasn't a Wikipedia? Dark ages, man. The error might simply have been an honest mistake, believing that the two terms were identical.

Heck, for all I know, the two terms were identical on May 12th, 1999, and the difference in definitions only evolved later, as it became obvious that there was a need to disambiguate between eager/deterministic and lazy/nondeterministic cleanup methods. Anyone who has more historical perspective on this than I do, feel free to chime in here.

Guess #2 is that on May 12th, 1999, the language design committee wished to leave open the possibility that a C# "destructor" could be implemented as something other than a CLR finalizer. That is, the "destructor" was designed to be a C# language concept that did not necessarily map one-to-one with the CLR’s "finalizer" concept.

When designing a language at the same time as the framework it sits atop is also being designed, sometimes you want to insulate yourself against late-breaking design changes in your subsystems. Deliberately preventing name conflation is one way to do that.

What’s your sudden obsession with May 12th, 1999 about?

The language committee's notes for May 12th 1999 read in part:

We're going to use the term "destructor" for the member which executes when an instance is reclaimed. Classes can have destructors; structs can't. Unlike in C++, a destructor cannot be called explicitly. Destruction is non-deterministic – you can't reliably know when the destructor will execute, except to say that it executes at some point after all references to the object have been released. The destructors in an inheritance chain are called in order, from most descendant to least descendant.  There is no need (and no way) for the derived class to explicitly call the base destructor. The C# compiler compiles destructors to the appropriate CLR representation.  For this version that probably means an instance finalizer that is distinguished in metadata. 

Notice that this supports my hypothesis that the language design team was attempting to insulate themselves from becoming tied to a particular CLR term.

  • Guess #3: first a decision was made to use the C++ ~ClassName() syntax for it (likely for the same reasons why colon is used for base class list etc), and then it was called "destructor" because that's what a thing that looks like that is called in C++.

  • "The C# spec" isn't an unambiguous term either, of course... there's the Microsoft version and the ECMA version. ECMA-334 (4th edition) has this to say in section 17.12:

    [Note: In the previous version of this standard, what is now referred to as a "finalizer" was called a

    "destructor". Experience has shown that the term "destructor" caused confusion and often resulted to

    incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a

    determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use

    Dispose. end note]

    Further points of confusion I've noted in the past: due every value type have a parameterless constructor or not? It does according to C#, but not according to the CLI spec. Then there's the concept of a static constructor, which doesn't exist in the CLI - the distinction that C# makes between a type with a static constructor and one with only static variable initializers is only present in terms of the beforefieldinitflag (AFAIK).

    It's a good job this doesn't impact on regular developers most of the time :)

  • It is interesting that you are concerned about the historical reason why something is wrong in Software Engineering. I personally consider the "historically why" irrelevant when it comes to Software. All that matters is what is correct so you can do my job well. No one will pay you on a job to tell them why var is not a keyword as some author implicitly imply. However, many will pay you to use it in your code.

  • In my opinion, the choice of "~ClassName" as the _finalizer_ syntax is single worst mistake in the design of C#. It needlessly confuses people.

    C++/CLI has a very elegant solution: a new syntax ("!ClassName" ) for a new concept.

  • @Zoldello:

    Understanding the historical perspective may not be very important for fixing today's problem. But it is QUITE important for making sure that we don't repeat the mistakes of the past.

    Now, I'm not sure that Eric is going to be designing a new language anytime soon. But I certainly hope that anyone who *is* planning to design a new language is reading Eric's blog because of wonderful little tidbits like this that help elucidate the pitfalls and details that are important for an undertaking as difficult as language (and compiler) design.

  • It is called a destructor because syntactically, that is what it is in a c-style language.  Whether or not c# has deterministic finalization is another thing entirely.  The using pattern merely mimics the behavior of  deterministic desctruction and relies on proper implementation to ensure that later finalization attempts are suppressed.

  • > due every value type have a parameterless constructor or not? It does according to C#, but not according to the CLI spec.

    There's no contradiction here, since two concepts need not map one-to-one. So every value type has a parameterless public constructor in C#, but such a constructor needs not be represented by a constructor on IL level.

  • @Pavel: I agree that the two concepts don't *have* to map... it's just surprising when they don't.

  • I remember that the issue of determinsitic vs. non-deterministic destruction caused quite a few of the more heated discussions back in late 2000/early 2001, when .NET was still beta.

    See http://groups.google.de/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/55053af9d6ffaac4/6cf64cc6affd0778?hl=de&q=c%23+destructor+finalizer+%22alexander+jung%22 for example, with Eric Gunnerson among the participants :-D

    Eventually those discussions lead to the introduction of the disposable pattern (but we never got determinsitic destruction).

    Anyway, back then most developers looking into C# had C++ background, and where certainly alienated by this semantic change (especially since it invalidated a lot of idioms we were used to). However I'm a little surprised that this is even an issue today, a decade later...

  • Its my pet peeve with C#, that C++ style destructors have gone and we have to use the using keyword to get the same automatic disposal.

    My big wish for the next version of C# is that if a struct implements IDisposable (or a new similar interface) and is used as a local variable, an automatic using block is inserted reaching to the end of that variable's scope.

    To make it work, we would need copy constructors to be called when one is used as a value parameter and insert calls to .Dispose before an over-writing assignment or being passed into an out parameter. Basically do what C++ does.

    You are the C# wish fairy, aren't you? I also want a pony.

  • "Yes, by these definitions, the C# spec gets it wrong. What we call a “destructor” in the spec is actually a finalizer, and what we call the “Dispose()” method invoked by a “using” statement is in fact a “destructor”. "

    Wrong actually. Destructor, as in C++, is always called except for pathological conditions like throwing exceptions from one destructor when unwinding is already in progress etc. 'using( )' on the other hand is entirely optional in C#. Best a compiler can do is generate a warning, or some some tool like FxCop yell at you that you forgot to dispose a disposable object.

    If C# wants to promote using( ) as a true destructor best approach would be to do what C++/CLI did. Reference types with stack allocated syntax and calling Dispose automatically upon exiting lexical scope if the type implements IDisposable.

  • If value types were allowed to have destructors they could be deterministic and improve the language considerably (IMHO).


  • Motti: what sort of value types would use destructors anyway? I don't think I've ever seen one.

  • @zoldello

    *You* may not get paid for those things, but examining previously-made decisions in language design and observing the impact they have had on users is, in fact, one of the things *Eric* gets paid for. Why would it surprise you to know that Eric puts some thought into it?

    In addition, as a programmer, knowing the reasoning behind certain idioms, concepts, and so forth helps determine their advantages and disadvantages in practice. To pick a grand and well-worked over example, we use OOP because historically, straight-up procedural programming had several disadvantages when used in the large. Ignoring the why of this tends to lead to things like Eric's oft-mentioned "object happiness", or difficult-to-maintain procedural programming in disguise.

  • Come to think of it, isn't having destructors useless without being able to override the assignment (=) operator? I think it's a really messy way to avoid some using()s or Dispose() calls.

Page 1 of 2 (20 items) 12