12

According to this, it states that Destructors cannot be inherited or overloaded. In my case, for all subclasses, the destructors will be identical. Is this pretty much telling me that I must define the same destructor in each sub class. There is no way that I can declare the destructor in the base class and have the handle the destruction? Say I have something like this:

class A
{
    ~A()
    {
        SomethingA();
    }

}

class B : A
{

}

B b = new B();

When B is destroyed, its destructor wont be called?

Yuck
  • 49,664
  • 13
  • 105
  • 135
user489041
  • 27,916
  • 55
  • 135
  • 204
  • Seems like something that would be easy enough to test. Do you have examples where the destructor for `b` isn't being run? – Yuck Nov 17 '11 at 21:11
  • @Yuck Well this leads me to possibly another dumb question... "When does the destructors actually get called? Is it on garbage collection, when the variable falls out of scope? – user489041 Nov 17 '11 at 21:13
  • 1
    That's a different question. I'd encourage you to post it as such, but it's been asked so frequently already... long story short: On garbage collection, but that may occur *much* later or *not at all*. –  Nov 17 '11 at 21:14
  • 4
    Better read up on destructors and why _not_ to use them. I suspect you don't need one at all. – H H Nov 17 '11 at 21:17

7 Answers7

43

According to this, it states that Destructors cannot be inherited or overloaded.

Correct. Destructors are not inheritable members, and are not virtual and so cannot be overridden. They always have the same signature so they cannot be overloaded.

In my case, for all subclasses, the destructors will be identical.

The fact that you are asking such a basic question is telling me that you should not be implementing a destructor in the first place. Implementing a destructor correctly is one of the hardest things to do in C# in all but the most trivial cases. Why do you believe that you need to implement a destructor?

Is this pretty much telling me that I must define the same destructor in each sub class?

No, not at all. How did you arrive at that conclusion from the fact that destructors are not inherited?

There is no way that I can declare the destructor in the base class and have the handle the destruction?

Sure, that's a sensible thing to do, provided that you're bent on implementing a destructor in the first place.

When B is destroyed, its destructor won't be called?

That is incorrect.

It occurs to me that it would have taken you a lot less time to try it yourself than to ask the question here and wait for a response.

When does the destructors actually get called? Is it on garbage collection, when the variable falls out of scope?

My earlier conjecture is correct. You definitely should not be implementing a destructor until you deeply understand the entire garbage collection process. The fact that you believe that variables are collected when they fall out of scope, for example, indicates that you don't understand this deeply enough to write a correct destructor.

When an object is determined to be unreachable from a gc root by the collector, and the object has a finalizer that has not been suppressed then the object is promoted to the next generation by placing it on the finalization queue for servicing by the finalizer thread. If not, its memory is reclaimed.

When the finalizer thread gets around to running, it runs all the destructors of the object. (Destructors will run in order from most derived to least derived.) After that process the object then may or may not be unreachable and finalization may or may not be suppressed. If the object is determined to be unreachable then the whole process starts again.

I cannot emphasize enough how well you need to understand the GC process in order to do this correctly. When you write a destructor it runs in an environment where nothing makes sense. All the references in the object might be to objects that are only rooted by the finalizer queue; normally all references are to live things. References might be to objects that are already finalized. Destructors run on a different thread. Destructors run even if the constructor failed, so the object might not even be constructed properly. Fields of non-atomic value types may be only partially written -- it is entirely possible for a double field to have only four of its bytes set by the constructor when the thread is aborted; the finalizer will see that partially-written field. Destructors run even if the object was placed in an inconsistent state by an aborted transaction. And so on. You have to be extremely defensive when writing a destructor.

This answer might also help:

When should I create a destructor?

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    What does something not being virtual have to do with it not being able to be overloaded? Or did you mean to say overridden? – Fernando Nov 17 '11 at 21:19
  • 2
    What do you mean "references may be to dead things". All object references are guaranteed to point to valid .net objects; if any of the objects to which one has direct or indirect references override Finalize and have not suppressed finalization, it's likely that they may have run or be scheduled to run, but as long as an object is registered for finalization, neither it nor any object to which it holds a direct or indirect reference can be removed from memory. The biggest difficulty with finalization is probably the unknown threading context. – supercat Dec 27 '11 at 23:52
  • 1
    @supercat: Good point. Let me clarify. Suppose you have two unrooted finalizable objects that mutually reference each other. The GC considers them garbage and puts them on the finalizer queue, so they are still (barely) alive. After the finalizer of the first one runs, the second finalizer is code running in an object that refers to the first object, an object that is barely alive and has already been finalized. It seems dangerous for the second object to make any assumption about the first at this point. – Eric Lippert Dec 28 '11 at 00:25
  • 2
    An object should not make any assumptions about the state of finalizable objects whose code one does not control, but if Class1 and Class2 are both in the same assembly, and Object1 (of type Class1) holds a reference to Object2 (of Class2) and accesses it within its finalizer, it must be prepared for the possibility that Object2 may have already been finalized, or may be pending finalization. If any references to Object2 have been exposed externally, it must also allow for the possibility that external live references to Object2 may still exist. Subject to those constraints, however, ... – supercat Dec 28 '11 at 01:02
  • 2
    ...it's perfectly possible and reasonable for objects to have finalizers that coordinate with each other. It took awhile for me to understand how a GC cycle divides objects into those which have roots other than the finalization mechanism, those which are registered for finalization but have no *other* roots, and those which have no rooted references at all, and to understand that finalization does not disturb an object in any way except for calling a Finalize (which will only disturb an object in ways it's explicitly programmed to do). – supercat Dec 28 '11 at 01:10
  • 7
    Hey Eric, I've flagged your answer as rude and if you read it again, I think you'll agree. Perhaps you were having a bad day? :) OP seems to be coming from C++ which would make this question very reasonable. – Seth Jan 15 '17 at 21:16
  • 2
    @Seth: I stand by the answer. I never said the question was unreasonable. The answer is accurate and helpful. – Eric Lippert Jan 16 '17 at 14:13
18

It's not a destructor in C#. It's known as a Finializer; and when it is called is non-deterministic. You actually can't count on it to be called at all.

Finalizers are used as a last resort to clean up unmanaged resources. You should look into the Dispose pattern.

harlam357
  • 1,471
  • 1
  • 13
  • 26
  • 2
    I second the notion of using IDisposable in most of these situations – Didaxis Nov 17 '11 at 21:14
  • 6
    Technically the C# spec calls it a destructor. (Personally I think that's a misleading name and it should be called finalizer even in the C# spec) – CodesInChaos Nov 17 '11 at 21:23
  • Even Eric Lippert [laments](http://blogs.msdn.com/b/ericlippert/archive/2010/01/21/what-s-the-difference-between-a-destructor-and-a-finalizer.aspx) that it's called a destructor in the spec. – vcsjones Nov 17 '11 at 21:24
  • 1
    @CodeInChaos: I agree, but we're stuck with it now. – Eric Lippert Nov 17 '11 at 21:26
  • Stating "It's not a destructor in C#" is not helping. – H H Nov 17 '11 at 22:07
  • A "destructor" is a C# construct which overrides Finalize() with a method that wraps the supplied bit of code and calls Base.Finalize(). The term "destructor" adds nothing but confusion, and the C# construct adds no discernable value over simply not forbidding overrides of `Object.Finalize`, but the term is what it is, and there is no other term that refers precisely to the syntactic entity which is written using destructor sequence, nor to the silly wrapper code which is generated by that syntax. – supercat Dec 27 '11 at 23:59
7

The finalizer you defined in A will be called when an instance of B is destroyed.

If you define a finalizer in both A and B, the most specific finalizer (B) will run first, then the least specific (A).

driis
  • 161,458
  • 45
  • 265
  • 341
2

A quick console app can help test this sort of thing.

using System;

class A
{
    ~A() => Console.WriteLine("~A");       
}

class B : A
{
    ~B() => Console.WriteLine("~B");
}

public class Program
{
    public static void Main() => new B();        
}

The output might be...

~B
~A
billpg
  • 3,195
  • 3
  • 30
  • 57
2

I've been doing .NET programming for close to a decade. The only time I implemented a finalizer, it ended up being a cause for a memory leak and nothing else. You almost never need them.

zmbq
  • 38,013
  • 14
  • 101
  • 171
  • I've found them very occasionally helpful for debug versions of software. E.g, `~MyClass() {if (this.Oops) Debug.Fail("Oops");}` – Brian Nov 17 '11 at 21:44
  • This is not an answer to the question. And just because you may have been working on projects that don't often have unmanaged resources doesn't mean that's the case for everyone using .Net. I would agree that the vast majority of C# objects don't need finalizers, but there are still many cases where they are necessary to prevent resource leaks (or deadlock, etc.) when something unexpected happens that prevents an object from being disposed normally. – reirab Jul 15 '20 at 14:55
1

If you define a destructor for B, it will be called, followed by A's. See the example at the bottom of the link you provided.

Didaxis
  • 8,486
  • 7
  • 52
  • 89
0

Well, I don't know about destructors, but you have other useful methods for cleanup like Finalize() and Dispose() from IDisposable.

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • A C# destructor and the `Finalize` are pretty much equivalent. – CodesInChaos Nov 17 '11 at 21:33
  • Yes, the destructor implicitly calls Finalize(), but since this is a method of object, its behavior relative to inheritance/override is much more clear. – Tudor Nov 17 '11 at 21:37
  • 2
    @Tudor: Unfortunately, the makers of C# decided that since people, if allowed to do so, would probably override `Object.Finalize()` rather than using the spiffy C# destructor syntax, it was necessary to forbid overriding `Object.Finalize()`. Seems silly to me, but I didn't write the language spec. – supercat Dec 27 '11 at 23:55