Code as written will work starting from CLR2.0 as the CLR2.0 memory model guarantees that All stores have release semantics.
Release semantics: Ensures no load or store that comes before the fence
will move after the fence. Instructions after it may still happen before
the fence.(Taken from CPOW Page 512).
Which means that constructor initialization cannot be moved after the assignment of the class reference.
Joe duffy mentioned this in his article about the very same subject.
Rule 2: All stores have release semantics, i.e. no load or store may
move after one.
Also Vance morrison's article here confirms the same(Section Technique 4: Lazy Initialization).
Like all techniques that remove read locks, the code in Figure 7
relies on strong write ordering. For example, this code would be
incorrect in the ECMA memory model unless myValue was made volatile
because the writes that initialize the LazyInitClass instance might be
delayed until after the write to myValue, allowing the client of
GetValue to read the uninitialized state. In the .NET Framework 2.0
model, the code works without volatile declarations.
Writes are guaranteed to happen in order starting from CLR 2.0. It is not specified in ECMA standard, it is just the microsoft implementation of the CLR gives this guarantee. If you run this code in either CLR 1.0 or any other implementation of CLR, your code is likely to break.
Story behind this change is:(From CPOW Page 516)
When the CLR 2.0 was ported to IA64, its initial development had
happened on X86 processors, and so it was poorly equipped to deal with
arbitrary store reordering (as permitted by IA64) . The same was true
of most code written to target .NET by nonMicrosoft developers
targeting Windows
The result was that a lot of code in the framework broke when run on
IA64, particularly code having to do with the infamous double-checked
locking pattern that suddenly didn't work properly. We'll examine this
in the context of the pattern later in this chapter. But in summary,
if stores can pass other stores, consider this: a thread might
initialize a private object's fields and then publish a reference to
it in a shared location; because stores can move around, another
thread might be able to see the reference to the object, read it, and
yet see the fields while they are still i n an uninitialized state.
Not only did this impact existing code, it could violate type system
properties such as initonly fields.
So the CLR architects made a decision to strengthen 2.0 by emitting
all stores on IA64 as release fences. This gave all CLR programs
stronger memory model behavior. This ensures that programmers needn' t
have to worry about subtle race conditions that would only manifest in
practice on an obscure, rarely used and expensive architecture.
Note Joe duffy says that they strengthen 2.0 by emitting all stores on IA64 as release fences which doesn't mean that other processors can reorder it. Other processors itself inherently provides the guarantee that store-store(store followed by store) will not be reordered. So CLR doesn't need to explicitly guarantee this.