Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructor execution order?

Tags:

c#

destructor

I know that Destructors in c# do not have an execution order.

The following Structure i do use in several Classes, it's to Desturct instances and the Static Information:

public class MyClass
{
    private static readonly Destructor DestructorObject = new Destructor();

    ~MyClass()
    {
        Console.WriteLine("Destructor Called");
    }

    static void Main(string[] args)
    {
        var myClass = new MyClass();
    }

    private sealed class Destructor
    {
        ~Destructor()
        {
            Console.WriteLine("Static Destructor Called");
        }
    }
}

As I mentioned above the order of Destuctors is undifined. But as im using this construct in many classes I found out, that in each class there is a non changing order, which also remains even if I'm recompiling the application and running it again.

Means that a MyClass1 could alaways run ~MyClass1 first and another class MyClass2 could alawways run ~Destructor first.

As there clearly is a "hidden" order for each class, can I trust it?

like image 447
LuckyLikey Avatar asked Dec 19 '22 03:12

LuckyLikey


2 Answers

As there clearly is a "hidden" order for each class, can I trust it?

No, you cant. If you look at the the docs, they scream:

The finalizers of two objects are not guaranteed to run in any specific order, even if one object refers to the other. That is, if Object A has a reference to Object B and both have finalizers, Object B might have already been finalized when the finalizer of Object A starts.

Relying on an implementation detail like this as part of you normal flow of execution would be a very bad idea.

Seeing that for some reason you chose to use finalizers as a way of cleaning up static resources, you should first think if that is the right approach, taking into account everything destructors imply, and then at least implement IDisposable and give the caller a chance to dispose of resources, calling GC.SupressFinalize as well.

Using this as a common approach in your objects will also cause the objects to prolong their life, as they will be eligible for collection only once they move to the f-reachable queue, and relying on the finalizer thread to actually clean them up, which isn't guaranteed at all.

Eric Lippert has recently (18-05-2015) started a series called When everything you know is wrong talking about finalizer myths, I suggest you take a look.

Edit:

Funny, Erics second post (posted today) in the series answers this question:

Myth: Finalizers run in a predictable order

Suppose we have a tree of objects, all finalizable, and all on the finalizer queue. There is no requirement whatsoever that the tree be finalized from the root to the leaves, from the leaves to the root, or any other order.

like image 178
Yuval Itzchakov Avatar answered Dec 21 '22 18:12

Yuval Itzchakov


As screamed many times earlier, finalizers don't guarantee any order. You can't make any assumptions. You should assume the worst (i.e) it can be executed in any order.

As there clearly is a "hidden" order for each class, can I trust it?

Yes and no.

For normal objects order of execution of finalizers is unpredictable. So no. You can't rely anything in it.

For objects which inherits from SafeHandle, yes there is some ordering. For example: If you get two objects ready for finalization, one is derived from SafeHandle and other is not, then it is guaranteed that the finalizer of object which doesn't inherit from SafeHandle will be executed before the SafeHandle's finalizer is executed.

This ordering exist for a reason which is described in this blog post. This isn't documented but blogged by BCL team. It is unlikely to be changed in future. But...

To prove it, the following program will always finalize Destructor class first then MyClass. Because you know MyClass inherits from SafeHandle.

public class MyClass : SafeHandle
{
    private static readonly Destructor DestructorObject = new Destructor();

    ~MyClass()
    {
        Console.WriteLine("Destructor Called");
    }

    protected override bool ReleaseHandle()
    {
        return true;
    }

    public override bool IsInvalid
    {
        get { return false; }
    }

    static void Main(string[] args)
    {
        var myClass = new MyClass(IntPtr.Zero, true);
    }

    private sealed class Destructor
    {
        ~Destructor()
        {
            Console.WriteLine("Static Destructor Called");
        }
    }

    public MyClass(IntPtr invalidHandleValue, bool ownsHandle)
        : base(invalidHandleValue, ownsHandle)
    {
    }
}
like image 21
Sriram Sakthivel Avatar answered Dec 21 '22 16:12

Sriram Sakthivel