Take a look at this little program skeleton:

class Foo { private int x; private int y; public Foo(int x, int y) { this.x = x; this.y = y; SideEffects.Alpha(); // Notice: does not use "this" } ~Foo() { SideEffects.Charlie(); } } static class SideEffects { public static void Alpha() { ... } public static void Bravo() { ... } public static void Charlie() { ... } public static void M() { Foo foo = new Foo(1, 2); Bravo(); } }

Let’s suppose we have three side effects: Alpha , Bravo and Charlie . What precisely they are is not important. The question is: what do we know about the order in which Alpha , Bravo and Charlie execute when a call to M() occurs? First off, clearly Alpha must happen before Bravo . The C# compiler and the jit compiler are not permitted to make any optimization that would cause the side effects of a single-threaded program to appear out of order. The construction of foo must be complete before control is passed to Bravo , which means that Alpha has already run.

You might think that Charlie has to happen after Bravo by the following reasoning: Charlie does not run until the object referred to by foo is garbage collected. The local foo is alive until the end of M and therefore it cannot be collected until after Bravo . This reasoning is incorrect. The C# compiler and the jitter are both permitted to notice that foo is never read from after its initial write, and therefore it can be garbage collected before the end of M , and therefore can be garbage collected before Bravo .

But didn’t I just say that the compilers were not permitted to make this optimization? No, I did not. I said that they were not permitted to make this optimization in a single-threaded program, but the garbage collector and the finalizer queue run on their own threads! The C# language makes very few guarantees about how side effects are ordered in a multi-threaded program; only very special side effects like volatile writes, thread creation, and so on, are guaranteed to be observed in a particular order. So it is legal for Charlie to happen before — or during! — Bravo .

But surely Charlie happens after Alpha ? Nope! This isn’t guaranteed either. The jitter can notice that foo is never read from and is therefore useless; it can throw it away entirely. The jitter can then notice that neither the reference to this nor fields x and y are ever used after this.y = y; , and mark the object as a candidate for finalization before Alpha . It is possible for an object to be destructed on the finalizer thread while its constructor is running in a user thread!

This is of course extremely unlikely, but it is legal, and therefore you have to assume the worst. This is yet another reason why it is so difficult to write a correct destructor in .NET; you can’t assume that the constructor finished before the destructor runs, so any invariants that you’re setting up in your constructor are not necessarily valid in the destructor.

So what do you do if you’re in this crazy situation and you require Charlie to run after Bravo ?

public static void M() { Foo foo = new Foo(1, 2); Bravo(); GC.KeepAlive(foo); }

The GC.KeepAlive method is a very special method that tells the jitter and the garbage collector that the lifetime of foo must be extended to at least this point. Make sure you understand that: when you call KeepAlive , the object is alive before the call and possibly dead after the call. Of course, the far better thing to do would be to make Foo expose its cleanup (possibly via IDisposable if the side effects in question are cleaning up an unmanaged resource) and simply say:

public static void M() { Foo foo = new Foo(1, 2); Bravo(); foo.Close(); }

and then have Charlie run in the Close . Now we’re on one thread again and the side effects must be correctly ordered.

If the order of the calls is important for the correct functioning of the program then let’s write a program that obviously calls them in the right order.