What is the best way for declaring static fields

Today I wrote a simple application, which shows what the difference is between initializing a static field in the explicit static constructor and doing the same during the declaration time (inline). I know that even when we initialize our static fields inline, the compiler will add an implicit static constructor to that class and initialize static values in that method. But this example showed me that there is really a long time difference between initializing static fields inline and in explicit static constructor. Here are a screenshot from the example - in this example BeforeFieldInit means that the static fields are initialized inline and NotBeforeFieldInit means that static fields are initialized in the explicit static constructor:

Screen Capturing Example

Download Example Code

Declaring a static field and initializing its value in an explicit static constructor.

using System;

namespace
BeforeFieldInitExample
{
    public class
NotBeforeFieldInit
    {
        // here there is only decleration of the static field
        public static int
A;

        static
NotBeforeFieldInit()
        {
            // Initialization of A's values is here in the explicit static constructor
            A = 0;
        }
    }
}


Initializing a static fields value during the decleration (inline).

using System;

namespace
BeforeFieldInitExample
{
    public class
BeforeFieldInit
    {
        public static int
A = 0;
    }
}


These two code groups will work as the same and produce the same result. Actually when we compile the second code, the output will be as the same with the first code. I mean, during the compiling time the compiler will create an implicit static constructor for the BeforeFieldInit class and initialize the field's value in that method. So it will be the same with the first code.

But there is a small difference between these codes. The difference can only be seen by checking the IL codes of these types.

For the first example, IL code of the class NotBeforeFieldInit will be as follows:

.class public auto ansi NotBeforeFieldInitClass
            extends
object
{
    .
method public hidebysig specialname
            rtspecialname instance
            void .ctor() cil managed
    {
        // Code Size: 7 byte(s)
        .maxstack
8
        L_0000: ldarg.0
        L_0001: call instance
void object::.ctor()
        L_0006: ret
    }

    .
method private hidebysig specialname
            rtspecialname static
            void .cctor() cil managed
    {
        // Code Size: 22 byte(s)
        .maxstack
8
        L_0000: nop
        L_0001: ldc.i4.0
        L_0002: stsfld int32 BeforeFieldInitExample.NotBeforeFieldInitClass::A
        L_0007: ldc.r8 9
        L_0010: stsfld float64 BeforeFieldInitExample.NotBeforeFieldInitClass::B
        L_0015: ret
    }

    .field public static
int32 A
    .
field public static
float64 B
}


And for the second example, which the name of the class is BeforeFieldInit's IL code will be as runs:
.class public auto ansi
            beforefieldinit // here beforefieldinit mask is added
                BeforeFieldInitClass

        extends object
{
    .
method public hidebysig specialname
            rtspecialname instance
            void .ctor() cil managed
    {
       // Code Size: 7 byte(s)
        .maxstack
8
        L_0000: ldarg.0
        L_0001: call instance
void object::.ctor()
        L_0006: ret

    }

    .method private hidebysig specialname
            rtspecialname static
            void .cctor() cil managed
    {
        // Code Size: 21 byte(s)
        .maxstack
8
        L_0000: ldc.i4.0
        L_0001: stsfld int32 BeforeFieldInitExample.BeforeFieldInitClass::A
        L_0006: ldc.r8 9
        L_000f: stsfld float64 BeforeFieldInitExample.BeforeFieldInitClass::B
        L_0014: ret

     }

    .field public static int32 A
    .
field public static
float64 B
}

IL codes generated by Xenocode Fox 2007

As you see in the IL the only difference between these two codes is the second class's having the beforefieldinit flag. So what is this flag doing?

beforefieldinit flag tells to the JIT that this class's static fields are initialized inline. If a class has no beforefieldinit flag, then JIT compiler will automatically check whether explicit static constructor is called before. This means that whenever you want to try to access any static member of this class or try to create a new instance of this class, JIT will automatically calls the explicit static constructor. Therefore you will loose performance because whenever you try to access a static member, JIT also checks whether static constructor is invoked before. But in the second example if we add a beforefieldinit flag to our class, this means that our static members are initialized inline, so JIT does not need to check whether static constructor is invoked before. This is the main reason that causes performance difference between those two classes. When we wrote an explicit static constructor and initialize our fields' values, the compiler will not add beforefieldinit flag to that class. But if we initialize our static fields inline, then compiler will add an implicit static constructor and initialize our static fields there and also add the beforefieldinit flag to our class.

So always, try to use inline field initialization for the static fields if you can.

Comments

August 20. 2008 08:58 PM

I've tried it but I got different results !

In debug mode I get :
NotBeforeFieldInit: 115 ms
NotBeforeFieldInit: 114 ms
NotBeforeFieldInit: 117 ms
NotBeforeFieldInit: 115 ms
BeforeFieldInit: 47 ms
BeforeFieldInit: 45 ms
BeforeFieldInit: 47 ms
BeforeFieldInit: 46 ms


But When & run the release version outside VS.Net, I get :
NotBeforeFieldInit: 63 ms
NotBeforeFieldInit: 63 ms
NotBeforeFieldInit: 64 ms
NotBeforeFieldInit: 63 ms
BeforeFieldInit: 92 ms
BeforeFieldInit: 92 ms
BeforeFieldInit: 92 ms
BeforeFieldInit: 97 ms

Any idea about what's happening on?

gheribil
August 21. 2008 07:53 AM

Interesting,

Here is my results (Outside VS, in release mode)

NotBeforeFieldInit: 44 ms
NotBeforeFieldInit: 43 ms
NotBeforeFieldInit: 43 ms
BeforeFieldInit: 13 ms
BeforeFieldInit: 13 ms
BeforeFieldInit: 13 ms

Ozcan DEGIRMENCI
October 21. 2008 04:38 PM

inline initialization is good for fundamental types, but sometimes it's not good for complex or user defined types. Because, we may encounter some initialization errors (because of bugs etc.) that the debugger may not check. (especially, when developing windows forms)
Burak
October 21. 2008 05:46 PM

I didn't care about the initialization errors while writing this article. Also I also don't understand that why the debugger will check initialization errors? It is CLR's responsibility to manage errors not the debugger's. Anyway what I try to said in my article is;

If we don't initialize our static fields during declaration then CLR will always check that is this class's static fields initialized or not whenever we try to access any member on that class. So this will have a performance cost.

Of course it is not possible to initialize all static fields during the declaration, we may need some arguments of we may do some specific operations before initialization. This is why I said that "Try to use inline initialization if you can." even they are complex types.

Ozcan DEGIRMENCI
July 3. 2009 01:18 AM

One more question, is - how compiler makes sure that static fields with BeforeFieldInit are initialized.
I have some "strange" results when declaring static property in both classes and use these Properties instead static fields in your test - The timings are allways equals (VS and standalone).
I think this proves my assumption that static constructor call verification performed in both cases (with or without BeforeFieldInit), but when BeforeFieldInit is present this verification performed at the method start, not at each static member access.

Alexander
July 3. 2009 10:36 AM

Here what the MSDN says;

BeforeFieldInit: Specifies that calling static methods of the type does not force the system to initialize the type.

Ozcan DEGIRMENCI

Search

Calendar

<<  September 2010  >>
MonTueWedThuFriSatSun
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
View posts in large calendar

Disclaimer

© 2007 - 2009
Ozcan DEGIRMENCI
All rights reserved. The content can be used elsewhere given that the source is properly acknowledged.