bxdxmx3

きじれてじろあ なきがせすで あぷせとねでぶ

C#のstatic変数初期化タイミング

今までclassのstatic変数の初期化タイミングを誤解していたのでまとめてみる。
ずっとdll,exeが読み込まれたタイミングで初期化されると思っていた。
だが、実際は以下のようになる。
以下のサンプルコードを実行する。

class Hoge
{
    static Hoge hoge = new Hoge();
    public Hoge(){ Console.WriteLine("Hogeのコンストラクタ"); }
    static Hoge(){ Console.WriteLine("Hogeのstaticコンストラクタ"); }
    static public void Print(){ Console.WriteLine("HogeのPrint"); }
}

static void Main(string[] args)
{
    Console.WriteLine("Main実行開始");
    Hoge.Print();
    Console.WriteLine("Main実行終了");
}


実行結果

Main実行開始
Hogeのコンストラクタ
Hogeのstaticコンストラクタ
HogeのPrint
Main実行終了

結論

classのstatic変数は初めてclassが評価された時に実行される。



こちらが本編

static変数の初期化タイミングを調べていたら、
もっと深い話を見つけた。
http://d.hatena.ne.jp/u_1roh/20060902/1157172959

staticコンストラクタの有無と、
コンパイラの設定(コード最適化?)によって
beforefieldinitフラグが設定されて
MSILのコード出力が変わって来るんだと。*1


上記コードでstaticコンストラクタを削除して、
Releaseビルドで実行して見るとこうなる。

実行結果

Hogeのコンストラクタ
Main実行開始
Hogeのstaticコンストラクタ
HogeのPrint
Main実行終了

なんと

classが初めて評価される前に、変数の初期化が行われる!
勉強になりました。

ちなみにC++では

同じようなコードを書いて実行してみたところ、
classがはじめて評価されるタイミングで初期化が行われた。
ず〜っと初期化タイミングを間違えて覚えてたんですね。。。

#include <iostream>
using namespace std;

class Hoge
{
    static Hoge hoge;
public:
    Hoge(){ cout << "Hogeのコンストラクタ" << endl; }
    static void Print(){ cout << "print" << endl; }
};

int tmain()
{
    cout << "Main実行開始" << endl;
    Hoge::Print();
    cout << "Main実行終了" << endl;
}

*1:ちなみに、このフラグが設定されてると、static変数を参照するたびに初期化が行われているかチェックをするために多少パフォーマンスが落ちるらしい。