ネコのために鐘は鳴る

寺院に住み着くパソコ〇好き

(C#) static コンストラクタを手動で呼ぶ

方法だけ知りたい生き急いでいる人用

// using System.Runtime.CompilerServices;
RuntimeHelpers.RunClassConstructor(typeof(Hoge).TypeHandle);

以下説明

static コンストラクタが呼ばれない状況

C# の構造体やクラスには、インスタンスのコンストラクタとは別に、static コンストラクタを書くことができます。

public class Hoge
{
    // インスタンスコンストラクタ
    public Hoge() { ... }

    // static コンストラクタ
    static Hoge() { ... }
}

通常、static コンストラクタは、初めてインスタンスコンストラクタが呼ばれた時に最初の1回だけ実行されます。つまり、型に対して静的な初期化処理を書くことができます。

さらに、言語仕様として必ず1度のみ呼ばれることが保証されているためマルチスレッドにおいても排他処理などをする必要がなく、スレッドセーフに実行されます。

クラスの場合は最初のインスタンスnewされた時に必ず呼ばれますが、構造体の場合、実はインスタンスがあるにもかかわらず static コンストラクタが実行されない場合があります。

public struct MyData
{
    public int Value;

    // static コンストラクタ
    static MyData() => Console.WriteLine("static ctor called !!");
}

上記のような構造体があったとして、以下のコードは static コンストラクタが呼ばれません。

// Case 1
var array = new MyData[10];
Console.WriteLine(array[3].Value);  // Value : 0

// Case 2
var data = new MyData();
Console.WriteLine(data.Value);  // Value : 0

Case1 と Case 2 のどちらも、コンパイルが通って実行でき、MyData型のインスタンスが存在しているにもかかわらず static コンストラクタは実行されません。構造体なので、どちらのケースもメモリのゼロ初期化が行われているだけだからです。

ちなみにクラスの場合は、Case1は null で、インスタンスが存在していないため static コンストラクタが呼ばれないのは当然ですし、Case2 はきちんと呼ばれます。

構造体の静的な初期化処理を static コンストラクタで行っている場合、インスタンスを使う前に呼ばれていてほしいのに、これでは困るので手動で呼びたい。

手動で呼ぶ

以下の方法で手動で呼ぶことができます。

// using System.Runtime.CompilerServices;
RuntimeHelpers.RunClassConstructor(typeof(MyData).TypeHandle);

メソッド名がRunClassConstructorとか書かれていますが、構造体にも使えます。

この手動で呼ぶ方法は自動で呼ばれる条件と同じように、ただ1度のみスレッドセーフに実行されます。複数回呼んでも2回目以降は無視されます。また、既に自動で呼ばれた状態で実行しても、何も実行されません。

つまり、必ず static コンストラクタが呼ばれていてほしい場所の前にこれを書いておくと、実行されていることが保証できます。