11

Given:

public class MyClass
{
    private static readonly Dictionary<string,int> mydict = CreateDictionary();

    private static Dictionary<string,int> CreateDictionary() { ... }
}

Is this done synchronously? (i.e. can two quick instantiations of MyClass cause CreateDictionary() to be called twice?

neverendingqs
  • 4,006
  • 3
  • 29
  • 57
  • 2
    i don't think so. Should be easy for you to test. Simply add a random value to the dictionary, and instantiate the class twice. Then, in both instances, examine the value of the stored item. – Russell Uhl Mar 11 '15 at 18:16
  • 1
    @RussellUhl I'm more concerned about concurrency issues, and I wasn't confident I could set up a race condition. – neverendingqs Mar 11 '15 at 18:29

2 Answers2

10

Yes, it's thread safe. Is the C# static constructor thread safe?

Static constructors are guaranteed to be run only once per application domain, before any instances of a class are created or any static members are accessed. http://msdn.microsoft.com/en-us/library/aa645612.aspx

Static field initialization is part of the static constructor. The fact that the field is readonly doesn't change anything

Some IL code as requested (taken from Try Roslyn http://goo.gl/ayIMG0)

.method private hidebysig specialname rtspecialname static 
    void .cctor () cil managed 
{
    // Method begins at RVA 0x205f
    // Code size 11 (0xb)
    .maxstack 8

    IL_0000: call class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> MyClass::CreateDictionary()
    IL_0005: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> MyClass::mydict
    IL_000a: ret
} // end of method MyClass::.cctor

where .cctor is the special name of the static constructors. The call to CreateDictionary and the assignment to mydict are quite evident.

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • But there is no static constructor – CriketerOnSO Mar 11 '15 at 18:24
  • 2
    @CriketerOnSO Write a class, put there a static field with a field initializer, compile it, look at it with IlSpy in "IL" mode. You'll see a static constructor with inside the initialization code. – xanatos Mar 11 '15 at 18:25
  • @CriketerOnSO Or you can look at http://goo.gl/0yMJ68 (using http://tryroslyn.azurewebsites.net/) – xanatos Mar 11 '15 at 18:31
  • 1
    I find that the fact that a static constructor is emitted in this case kind of strange. To quote the spec: "If a static constructor (§10.12) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.". Also [Eric Lippert's blog post on static constructors](http://ericlippert.com/2013/02/14/static-constructors-part-three/) indicates that a static constructor would not be emitted. – Mike Zboray Mar 11 '15 at 18:35
  • @mikez: I did not intend to imply that no static constructor is emitted; as an implementation detail, yes, a static constructor is emitted. The difference is that the `beforefieldinit` flag is turned on. As for being "kind of strange", well, the whole feature is kind of strange. I find it bizarre and unfortunate that omitting an empty method can cause a semantic change in a program. – Eric Lippert Mar 11 '15 at 18:45
  • @EricLippert Yes. I was playing with IL and I just noticed that beforefieldinit flag. Thanks for confirming. – Mike Zboray Mar 11 '15 at 18:46
  • @mike: I know this is splitting semantic hairs, but at the CLI-level there are no static constructors, only type-initializers; static constructors are a C#-level concept. So Eric's blog post is correct: [...] – LukeH Mar 12 '15 at 16:18
  • @LukeH That then in IL source code are called *cctor* :-) (class-constructor) – xanatos Mar 12 '15 at 16:21
  • [...] If your C# code has a static constructor then the emitted IL will contain a type-initializer which does the work of that static constructor (and the type will be marked `beforefieldinit`). If your C# code does not have a static constructor then the emitted IL *might* or *might not* contain a type-initializer, depending on static field initialisation etc in your code (and the type will *not* be marked `beforefieldinit`). – LukeH Mar 12 '15 at 16:23
  • @xanatos: As I said, this is really just semantic nitpicking. The `cctor` is the type-initializer. You won't find the phrase "static constructor" anywhere in the [CLI specification](http://www.ecma-international.org/publications/standards/Ecma-335.htm). Admittedly there's a huge overlap between what type-initializers and static constructors do, but there are subtle differences. – LukeH Mar 12 '15 at 16:29
  • @LukeH It's a valid point, quite like the argument over whether `~Class` is a "destructor" or a "finallizer". Properly, in C#, it is destructor. The C# spec makes no mention of finalizers (other than in relation to `GC.WaitForPendingFinalizers`), while at the runtime level the method is properly called a finalizer. The mistake we me trying to read between the lines about the IL implementation when only behavior was being described in both the C# spec and Eric's post. – Mike Zboray Mar 12 '15 at 17:15
  • @xanatos It would be nice to add the code generated by the compiler to make the answer better :) – dburner Apr 09 '15 at 07:24
8

The accepted answer is correct; the initialization will happen either zero or one times but never twice. But I would add a caveat. In your example the CLR and the C# language reserve the right to initialize the field earlier than you might expect. If you write:

public class MyClass
{
    private static readonly Dictionary<string,int> mydict = CreateDictionary();
    static MyClass() {}

then the CLR and C# guarantee that the field will be initialized when the first static method is called on MyClass or when the first instance of MyClass is created. If you omit the static constructor then the CLR and C# are permitted, but not required, to initialize the field at any time prior to those events. In particular, suppose you have a method M which calls a static method of MyClass. The CLR might decide to run the static initializer of MyClass.mydict when M is jitted, and not when M actually calls the static method. This can in some rare situations lead to surprising results.

Do a web search on the beforefieldinit optimization for more details. Jon Skeet has a good article on this.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Would this be an example of something that could lead to different results? http://pastebin.com/v2S4A8gf – neverendingqs Mar 11 '15 at 19:04
  • 1
    Please, update the link reported in your last comment, since NodaTime is now hosted by GitHub. I think Jon's comments start now at line 897, here: [nodatime/CalendarSystem.cs](https://github.com/nodatime/nodatime/blob/master/src/NodaTime/CalendarSystem.cs) – Luca Cremonesi Mar 16 '15 at 14:32