83

C# has static constructor which do some initialization (likely do some unmanaged resource initialization).

I am wondering if there is static destructor?

Pang
  • 9,564
  • 146
  • 81
  • 122
user496949
  • 83,087
  • 147
  • 309
  • 426
  • 1
    +1 for this funny comment, leppie. And I would like to point out that static resources have mostly the same lifetime as the the application. They die when application dies. So, there is no need of a static destructor. – decyclone Dec 06 '10 at 09:37
  • 5
    "so there is no need of a static destructor" -- how are those two things related? Because it would only happen when the appdomain is being unloaded it's suddenly not necessary? I'm not sure I follow the logic there. – BrainSlugs83 Nov 28 '13 at 03:11

6 Answers6

117

Not exactly a destructor, but here is how you would do it:

class StaticClass 
{
   static StaticClass() {
       AppDomain.CurrentDomain.ProcessExit +=
           StaticClass_Dtor;
   }

   static void StaticClass_Dtor(object sender, EventArgs e) {
        // clean it up
   }
}
Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
zackery.fix
  • 1,786
  • 2
  • 11
  • 20
  • 3
    Though the event _seems_ to be on AppDomain it is in fact only fired when the default domain exits (which is, the last domain), right before the whole application dies. It _will not fire_ when any other AppDomain is unloaded, which in itself will cause most types and instances of types to be destructed (but not always). This event is not [guaranteed to always fire.](http://blogs.msdn.com/b/jmstall/archive/2006/11/26/process-exit-event.aspx). – Abel Dec 14 '15 at 16:12
  • 1
    I recommend not leaving sensitive cleanup operations up to the deconstruction of a static instance and following this recommendation, you would never need to worry about this event not firing. I have never had a problem where this event did not fire (even when unexpected exceptions in the application have occurred). Killing a process through task manager is a lot different than an unexpected exception. You are right though, it will not fire if the user kills the program though the OS task management system. – zackery.fix Dec 16 '15 at 11:54
  • 1
    Would that be a good idea to add the destruction code to the end of the Main() method, from where the app domain is created? – florien Oct 15 '17 at 20:10
78

This is the best way (ref: https://stackoverflow.com/a/256278/372666)

public static class Foo
{
    private static readonly Destructor Finalise = new Destructor();

    static Foo()
    {
        // One time only constructor.
    }

    private sealed class Destructor
    {
        ~Destructor()
        {
            // One time only destructor.
        }
    }
}
Community
  • 1
  • 1
Tod Thomson
  • 4,773
  • 2
  • 33
  • 33
  • 5
    This way is guaranteed to work! :) Give it a go, I have unit tests that prove that it works if anyone is interested? – Tod Thomson Oct 30 '13 at 03:19
  • 1
    And it's an environment independent way (builded inside language logic) – eMeL Feb 16 '17 at 15:45
  • 10
    Unfortunately, this is *not* guaranteed to work. Per Raymond Chen's article about [Garbage Collection](https://blogs.msdn.microsoft.com/oldnewthing/20100809-00/?p=13203/) finalizers are not reliable since there is no guarantee the GC will ever run. – McGuireV10 Nov 20 '17 at 17:07
  • @McGuireV10 (I am not saying you're wrong here), however the official documentation on Finalizers does not contain any mention of what Raymond is saying https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/destructors it would be interesting to hear from Microsoft on whether his article from 2010 is (or still is) accurate? In all of my testing of the above I have _never_ been able to trigger a case where the finalizer doesn't get called... If my above advice is incorrect, then I'd like to update/remove it if that is the case, but I think we need stronger proof. – Tod Thomson Feb 22 '20 at 01:19
  • 2
    @TodThomson For .NET Framework the ~Object [Remarks](https://learn.microsoft.com/en-us/dotnet/api/system.object.finalize?view=netframework-4.8#remarks) section lists some cases where the finalizer is not guaranteed, and the doc you linked notes that they aren't called on shutdown under .NET Core. Unfortunately the quality of MS docs isn't quite what it used to be. – McGuireV10 Feb 22 '20 at 10:09
  • could you explain how to use that approach? should I call `Finalise` somewhere or it's supposed to be called automatically when `Foo` will be going to dead? – anatol Apr 30 '21 at 01:30
  • `Finalise` is the name of the (private static readonly) instance of the private sealed class `Destructor`. You don't need to explicitly call anything - you just substitute your "one time only constructor" and "one time only destructor" code in place of my comments. To see how it works in your applicaiton, put breakpoints on both methods and observe. – Tod Thomson Aug 25 '21 at 05:44
41

No, there isn't.

A static destructor supposedly would run at the end of execution of a process. When a process dies, all memory/handles associated with it will get released by the operating system.

If your program should do a specific action at the end of execution (like a transactional database engine, flushing its cache), it's going to be far more difficult to correctly handle than just a piece of code that runs at the end of normal execution of the process. You have to manually handle crashes and unexpected termination of the process and try recovering at next run anyway. The "static destructor" concept wouldn't help that much.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 6
    In my case, I was using a OS-wide Mutex that needed released, or it would throw an AbandonedMutexException on the next run of the application. In this case, the memory and handles released at the end of the program didn't account for my Mutex. What you stated in the 3rd paragraph could be a way to manage it, but an end of the application release seemed to make more sense for me. – user1132959 Jul 15 '14 at 14:20
  • In my case, it was a global message-passing (unmanaged) thread that had to be shut down properly at the end, otherwise it would crash. [zackery.fix's answer](https://stackoverflow.com/a/13258842/603828) worked great for me. – ulatekh Oct 11 '22 at 20:51
18

No, there isn't. The closest thing you can do is set an event handler to the DomainUnload event on the AppDomain and perform your cleanup there.

Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
Neil Knight
  • 47,437
  • 25
  • 129
  • 188
  • 4
    This is actually a much preferred approach to the higher upvoted `Process_Exit` suggestion, which is quite different. The lifetime of a type after static ctor has fired is usually the lifetime of the assembly, which gets unloaded with the AppDomain. – Abel Dec 14 '15 at 16:14
  • 1
    This was the simplest and least-impact solution for me. In my context, hooking into Process_Exit didn't work, but DomainUnload did. – Brian Wirt Jul 31 '19 at 22:51
  • But `DomainUnload` doesn't get called for the [default domain](https://stackoverflow.com/questions/2693160/)? – ulatekh Jul 07 '21 at 20:30
7

Initializing and cleaning up unmanaged resources from a Static implementation is quite problematic and prone to issues.

Why not use a singleton, and implement a Finalizer for the instance (an ideally inherit from SafeHandle)

Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
1

No there is nothing like destructor for static classes but you can use Appdomain.Unloaded event if you really need to do something

Rahul
  • 76,197
  • 13
  • 71
  • 125
TalentTuner
  • 17,262
  • 5
  • 38
  • 63