Zeph (./439) :
GoldenCrystal (./438) :
Heu…
J'ai vraiment du mal à te suivre là… Par quelle magie crois-tu que les champs (particulièrement les readonly
) sont initialisées s'ils ne sont pas placés dans le constructeur ? 
Peut-être qu'on parle de deux choses différentes, moi de ce qu'on appelle "constructeur" dans n'importe quel langage objet, donc ici le constructeur écrit en C# (et ce n'est *pas* lui qui s'occupe des initialisations des attributs), et le code de construction généré qui forcément doit s'occuper de tout le boulot, dans l'ordre (i.e. attributs puis constructeur).
Effectivement, pour moi celui qui compte surtout, c'est le constructeur .NET…

Heu… Mais quel est l'intérêt de Interlocked.Exchange ici ? 
Le lock fait toutes les barrières mémoire nécessaires au double-checked locking, c'est d'ailleurs l'exemple numéro 3 dans le lien que j'ai posté ! 
Il y a vraiment la garantie que même sans memory barrier ça fonctionne, quel que soit le processeur et la version de .NET ? Il me semblait avoir entendu que non, mais je n'ai pas de lien sous la main.
Ben, cf. ce les deux premiers liens que j'ai posté, normalement oui.
J'ajoute celui-là qui n'est plus disponible aujourd'hui

(je savais qu'il me manquait un truc !)
http://web.archive.org/web/20110605122922/http://msdn.microsoft.com/en-us/magazine/cc163715.aspx (Cet article est normalement relié aux autres mais bon… J'avais espéré le retrouver à une autre adresse et puis je l'ai oublié. Lui, est plus explicite)
C'est le plus proche d'une réponse 100% exacte que j'aie trouvé lorsque j'avais recherché ça à l'époque, et ça expliquait plus ou moins que le CLR 2.0 de Microsoft garantit des choses assez similaire au modèle x86. (Disons que dans le cas du double checked locking, tu pouvais prendre ça comme acquis que ça marcherait tout le temps)
Apparemment, les articles récents tendent à être plus contrastés:
http://msdn.microsoft.com/en-us/magazine/jj553518.aspxCet article n'est pas très explicite, mais à priori il n'y aurait pas autant de garanties qu'en x86. On peut supposer qu'une barrière mémoire est placée à la fin des appels de constructeurs pour conserver un comportement similaire à ce qui est observé sur x86, mais ce n'est pas dit clairement. Par contre il explique qu'il faut à présent penser à marquer les variables comme volatile là où on ne le faisait pas auparavant. (En .NET, volatile implique des demi-barrières sur la lecture et sur l'écriture… Ce mot-clé ne sert pas énormément en .NET/x86)
La partie qui nous intéresse :
We’ve done targeted work to strengthen the memory model on ARM—specifically, we’ve inserted memory barriers at key points when writing to the managed heap to guarantee type safety—but we’ve made sure to only do this with a minimal impact on performance. The team went through multiple design reviews with experts to make sure that the techniques applied in the ARM CLR were correct. Moreover, performance benchmarks show that .NET code execution performance scales the same as native C++ code when compared across x86, x64 and ARM.
If your code relies on lock-free algorithms that depend on the implementation of the x86 CLR (rather than the ECMA CLR specification), you’ll want to add the volatile keyword to relevant variables as appropriate. Once you’ve marked shared state as volatile, the CLR will take care of everything for you. If you’re like most developers, you’re ready to run on ARM because you’ve already used locks to protect your shared data, properly marked volatile variables and tested your app on ARM.
C'est maigre, mais en attendant mieux…
Dans le cas du double-checked lock, marquer la variable comme volatile devrait être plus que suffisant, si jamais ça devait être vraiment nécessaire. (Puisque dans ce cas, tu es sûr que l'assignation du champ sera fait après la dernière assignation dans le constructeur, donc ton objet sera initialisé)
Sinon, j'aurais plutôt utilisé Thread.VolatileWrite() pour ça ^^
J'ose supposer que c'est plus efficace parce que ça ne verrouille pas forcément la mémoire, contraîrement au Interlocked, mais bon, c'est une opération faite une seule fois ça ne fera pas beaucoup de différence. (De mémoire les Thread.VolatileXXX sont des barrières mémoire complètes)
Bref, le mieux pour l'instant c'est surtout de pas le faire soi-même !

C'est simple, plus rapide, et plus efficace, que demande le peuple :]
(Et le double-checked locking est fourni par Lazy<T> s'il faut vraiment y avoir recours ^^)