0

Just trying to understand why destructor is called outside of the scope of USING after instance is dispose. I know there is no need for destructor when IDisposable is implemented. Here is my example:

using System;

namespace Destructor
{
    class Program
    {
        static void Main(string[] args)
        {

            using (var a = new A())
            {
                Console.WriteLine("... inside USING ...");
            }

            Console.WriteLine("...END...");
        }
    }

    //............................................................

    class A : IDisposable
    {
        public A()
        {
            Console.WriteLine("...Constructor called...");
        }

        ~A()
        {
            Console.WriteLine("...Destructor called...");
        }

        public void Dispose()
        {
            Console.WriteLine("...Dispose called...");
        }
    }
}

Output:

...Constructor called...

... inside USING ...

...Dispose called...

...END...

...Destructor called...

Rishav
  • 3,818
  • 1
  • 31
  • 49
cembo
  • 1,039
  • 2
  • 9
  • 18
  • 2
    Because a ~destructor is a 'callback' from the GC. It is not (cannot be) triggered by using or Dispose. – H H May 20 '18 at 09:15
  • 1
    Most important thing to learn/remember: avoid destructors. You should never need them. See Safehandle. – H H May 20 '18 at 09:16
  • 2
    Using block makes sure that Dispose method is called so that all the unmanaged resources are removed from the memory safely. Destructor is called when the object is actually destroyed from the memory. Destructor is needed to ensure that you call dispose method in it before the object is destroyed so the unmanaged resources are removed for sure. This is useful when you are not using `using` block – Chetan May 20 '18 at 09:18
  • 1
    You forgot to add GC.SuppressFinalize() to your Dispose() method. In some languages that is automatic. but in C# you have to do it yourself. – Hans Passant May 20 '18 at 09:22
  • You wrote a buggy and wrong implementation of the disposable pattern and then asked why it was buggy and wrong. If you want to write a correct implementation of the disposable pattern then read up on the correct way to implement it, and then implement it correctly. – Eric Lippert May 20 '18 at 13:06
  • @Eric Lippert ... thanks, but as you can see I didn't ask why my implementation was wrong, I have asked why destructor was called outside using block when instance was already disposed by GC, nothing else ... – cembo May 21 '18 at 02:36
  • 2
    The question presupposes a falsehood; the instance was not disposed by the GC. No disposable object is **ever** disposed by the GC! So your question is "I believe something false; why is some other thing false?" Don't try to answer that question; instead, stop believing the false thing! Learn the truth about the garbage collector and then you won't have to ask about your false beliefs. – Eric Lippert May 21 '18 at 13:39
  • @Eric Lippert ... thanks man, very helpful :) ... – cembo May 26 '18 at 03:12

2 Answers2

6

To answer your question directly -

The Destructor is called by the Garbage collector The Garbage Collector thread puts the object reference on the finalizer queue, and the finalizer thread calls the finalizer (A.K.A. Destructor). This is why you should implement the IDisposable interface in the first place.

The using statement is actually syntactic sugar for try...finally - When you write

using(var x = new MyDisposableClass())
{
    //  code here
}

it's the same as writing:

var x = new MyDisposableClass()
try
{
    //  code here
}
finally
{
    (IDisposable(x)).Dispose();
}

So c# calls the Dispose method of your instance the second it hits the end of the using block.

However, this does not mean that this instance will get garbage collected that very second. In fact, it can live quite happily in the system until the garbage collector finally gets around to clear it. The Garbage collector runs on it's own thread, and the CLR decides when to activate it. You can activate it by calling GC.Collect() but that's ill advised in most cases.
Read When is it acceptable to call GC.Collect? for more details.

To add some more background - When you are using unmanaged resources you can release them either by writing code for that in the Dispose(bool) overload or by writing code for that in the Destructor.
Releasing them in the Dispose(bool) means you can control when they are released in the calling code (and you usually want to do that as soon as possible).

Releasing them in the Destructor means they only gets released (if and) when the finalizer thread executes the Destructor, meaning you can't control when they are released in your code.

Moreover, writing Destructors correctly is hard. So hard that it's best to avoid doing that in the first place.
In fact, it's so hard that Eric Lippert wrote a few blog posts about that, entitled "When everything you know is wrong".

There is some more helpful information in Reed Copsey's five part blog post series on IDisposable - starting with IDisposable Part 1 – Releasing Unmanaged Resources.

Bonus reading - Implementing a Dispose method

The Dispose(Boolean) overload In the second overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

The body of the method consists of two blocks of code:

  • A block that frees unmanaged resources. This block executes regardless of the value of the disposing parameter.

  • A conditional block that frees managed resources. This block executes if the value of disposing is true. The managed resources that it frees can include:

Managed objects that implement IDisposable. The conditional block can be used to call their Dispose implementation. If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

Managed objects that consume large amounts of memory or consume scarce resources. Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

And one last thing - you wrote "I know there is no need for destructor when IDisposable is implemented" - This is almost correct. You almost never should override the destructor for your types, as a good read of Mr. Lipper's blog indicates.

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • The first sentence is wrong. There is no "why" here. – H H May 20 '18 at 09:20
  • @HenkHolterman I disagree. I've rephrased it a bit but I still think that's the main reason why we have the `IDisposable` interface in the first place. – Zohar Peled May 20 '18 at 09:21
  • I think you implement IDisposable to _avoid_ needing a destructor. It is still part of the Disposable _pattern_ (not interface) but only because that is outdated. – H H May 20 '18 at 09:23
  • @HenkHolterman I'm still working on my answer, I think it's going to address this issue as well once it's done – Zohar Peled May 20 '18 at 09:27
  • There is no sense in having `Dispose(bool isDisposing) `. By doing so you are mixing managed and unmanaged resources handling logic in one place. Use (or implement, but I'd say this is not needed in any scenario, as far as there are ready to use wrappers in BCL) managed wrapper for your unmanaged resource and leave simple and straightforward `IDisposable` implementation in consumer classes. – Uladzislaŭ May 20 '18 at 09:39
  • @Vladislav [Microsoft seems to disagree with your statement.](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose) – Zohar Peled May 20 '18 at 09:40
  • @ZoharPeled Nope, if you read it carefully. "The dispose pattern is used only for objects that access unmanaged resources". While you should implement `IDisposable` to deal with **managed** resources all the time. Those guarantee that nothing will leak, but do so with use of finalizers, which are not optimal for performance – for this reason it's better to `Dispose` them manually. – Uladzislaŭ May 20 '18 at 09:46
  • @Vladislav If your class have members that implements the IDisposable interface themselves, such as System.Timers.Timer for instance, you should dispose them even though they are managed instances. Hence the Dispose(bool) overload. Official documentation is too long and complicatedly formatted to include in a comment so I've included that in my answer. – Zohar Peled May 20 '18 at 17:13
  • @ZoharPeled I'm totally agree that you should dispose them, you just don't need this `Dispose(bool)` overload, `Dispose()` is enough. – Uladzislaŭ May 20 '18 at 17:32
  • @Vladislav You don't *need* it, but that's the pattern recommended by official documentation, and with good reason. The `Dispose(bool)` is either called from the `Dispose()` overload or from the destructor - that's why it needs that `bool`. – Zohar Peled May 20 '18 at 17:59
  • @ZoharPeled you should not implement finalizer in class, that doesn't own unmanaged resources. It means in your scenario that `Dispose()` will be always a one-liner `Dispose(true)` and `Dispose(bool)` will always be called with `true`. That's senseless. And seems that I have nothing more to say. – Uladzislaŭ May 20 '18 at 18:08
  • I wrote you should aviod implementing finilizers in my answer, one of us does not fully understand the documentation. It might be me and might be you. – Zohar Peled May 20 '18 at 19:12
  • Note that it is not quite right that the GC calls the destructor. The GC thread puts the object reference on the *finalizer queue*, and the *finalizer thread* calls the finalizer. That's why to do a collection that finalizes you have to do both a `Collect` and a `WaitForPendingFinalizers`. – Eric Lippert May 22 '18 at 21:31
  • @EricLippert fixed, thanks! – Zohar Peled May 24 '18 at 09:08
1

You cannot know when the destructor is called. The destructor is called when the garbage collector detects that an object is eligible for collection. This happens at some undetermined period of time after the resource is not needed anymore.

You can read more detailed here: https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern

marius
  • 319
  • 1
  • 9
  • You shouldn't implement this pattern, until you want to use unmanaged resources. And as far as there are managed wrappers for almost everything, I'd just say you shouldn't implement this pattern. – Uladzislaŭ May 20 '18 at 09:27