Static Finalizer
Asked Answered
A

5

49

What is the right way to perform some static finallization?

There is no static destructor. The AppDomain.DomainUnload event is not raised in the default domain. The AppDomain.ProcessExit event shares the total time of the three seconds (default settings) between all event handlers, so it's not really usable.

Amieva answered 1/11, 2008 at 20:35 Comment(1)
First, in C#, we need to get out of the habit of using finalizer and destructor interchangably. One is deterministic, the other is not. It's interesting to note that the C# spec gets the terms backward from the CLR and other .NET language specs. It's also interesting to note that the C# language committee's notes explicitly say there are no foreseen reasons why C# can't have static finalizers. https://mcmap.net/q/129367/-the-difference-between-a-destructor-and-a-finalizerSip
P
33

Basically, you can't. Design your way around it to the fullest extent possible.

Don't forget that a program can always terminate abruptly anyway - someone pulling out the power being the obvious example. So anything you do has to be "best effort" - in which case I'd certainly hope that AppDomain.ProcessExit would be good enough.

What do you need to do, in your particular case?

Pender answered 1/11, 2008 at 20:40 Comment(4)
How does this answer relate to the answer by Michael (since it seems it is possible to have static finalizers)? (Either way I agree that finalizers are unreliable.)Wetzel
@mafutrct: Michael's answer isn't really a static finalizer. It's a static field with a reference to an instance which has a finalizer. It's not the same thing, even though it could have similar effects. I wouldn't use it personally.Pender
The true value of a static finalizer--from the perspective of a Delphi developer where such things are supported at the language level--isn't some nonsensical "guarantee that this will run," (as you point out, someone could always pull the plug,) but rather a guarantee that it will run when the class goes out of scope. If the assembly containing the class was loaded dynamically as a plugin, and gets unloaded before program termination, this can be a pretty significant distinction, and having a cleanup routine that runs at process exit is no good. How do you handle that scenario "correctly"?Television
@MasonWheeler: Well you'd normally have a plugin within its own AppDomain, in which case you could subscribe to the AppDomain.DomainUnload event. (There's no such concept as a class going "out of scope" as a class isn't in a scope to start with.)Pender
A
51

Herfried Wagner has written an excellent article explaining how to implement this – alas, in German (and VB). Still, the code should be understandable.

I've tried it:

static readonly Finalizer finalizer = new Finalizer();

sealed class Finalizer {
  ~Finalizer() {
    Thread.Sleep(1000);
    Console.WriteLine("one");
    Thread.Sleep(1000);
    Console.WriteLine("two");
    Thread.Sleep(1000);
    Console.WriteLine("three");
    Thread.Sleep(1000);
    Console.WriteLine("four");
    Thread.Sleep(1000);
    Console.WriteLine("five");
  }
}

It seems to work exactly the same way as the AppDomain.ProcessExit event does: the finalizer gets ca. three seconds...

Amieva answered 1/11, 2008 at 23:51 Comment(1)
This is the best answer as you can debug your "finaliser" in this way, whereas the AppDomain.ProcessExit delegate doesn't allow you to debug it (at least in my experience).Alon
P
33

Basically, you can't. Design your way around it to the fullest extent possible.

Don't forget that a program can always terminate abruptly anyway - someone pulling out the power being the obvious example. So anything you do has to be "best effort" - in which case I'd certainly hope that AppDomain.ProcessExit would be good enough.

What do you need to do, in your particular case?

Pender answered 1/11, 2008 at 20:40 Comment(4)
How does this answer relate to the answer by Michael (since it seems it is possible to have static finalizers)? (Either way I agree that finalizers are unreliable.)Wetzel
@mafutrct: Michael's answer isn't really a static finalizer. It's a static field with a reference to an instance which has a finalizer. It's not the same thing, even though it could have similar effects. I wouldn't use it personally.Pender
The true value of a static finalizer--from the perspective of a Delphi developer where such things are supported at the language level--isn't some nonsensical "guarantee that this will run," (as you point out, someone could always pull the plug,) but rather a guarantee that it will run when the class goes out of scope. If the assembly containing the class was loaded dynamically as a plugin, and gets unloaded before program termination, this can be a pretty significant distinction, and having a cleanup routine that runs at process exit is no good. How do you handle that scenario "correctly"?Television
@MasonWheeler: Well you'd normally have a plugin within its own AppDomain, in which case you could subscribe to the AppDomain.DomainUnload event. (There's no such concept as a class going "out of scope" as a class isn't in a scope to start with.)Pender
I
8

Two solutions that jump to mind:

  • Don't use a static class. If you use a non-static class and instantiate it, you don't have to worry about cleanup as much.
  • If that's not an option, I'd argue that this is a good situation to use a singleton. This will instantiate a copy of your object and have the finalizer called on exit, but still allow you to treat it like a static class for the most part. After all, your class is static already and therefore shares most of the common reasons not to use a singleton.
Ivetteivetts answered 1/11, 2008 at 20:59 Comment(0)
H
6

I would question what you are loading in your static methods that need to be released. I certainly wouldn't recommend doing these things in a static method.

That said, your static method could instanciate an object that has a finalise method.

Helman answered 1/11, 2008 at 20:40 Comment(0)
P
2

To port Michael Damatov's answer (C#) which is based on Herfried K. Wagner. (VB.NET) here is the C++/CLI version:

ref class MyClass
{
        ref class StaticFinalizer sealed
        {
            !StaticFinalizer();
        };
        static initonly StaticFinalizer^ stDestr = gcnew StaticFinalizer();
}

MyClass::StaticFinalizer::!StaticFinalizer()
{
    System::Diagnostics::Debug::WriteLine("In StaticFinalizer!");
}

P.S. Just like the AppDomain.ProcessExit method, this one may not be called if the process is terminated abnormally (from Task Manager for example). Another word of caution is that if MyClass is generic (templated), the assumption that its static constructor and static destructor will be called no more than once per application execution is no longer be valid.

Parrnell answered 31/7, 2014 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.