Type initializer についてまとめ

参考資料

  • ECMA-335 (4th edition)

Type initializer とは

Type initializer は CLI 仕様で規定されている、型それ自体を初期化するための特別なメソッドである*1

要するに(C# で言うところの) static メンバの初期化子や static コンストラクタを実行してくれるアレである。

CLI 仕様書(ECMA-335)の 10.5.3 (Type initializer) 以下では、下に述べる事柄が述べられている。

Type initializer の満たすべき条件

Type initializer には以下の制約が与えられている。これらは、普通の C# コンパイラを使ってコード生成する限りは気にする必要はないだろう。

  • static である
  • パラメタを持たない
  • 戻り値を持たない
  • rtspecialname, specialname を持ち、".cctor" という名前である

Type initializer で出来ること

Type initializer では、普通のメソッドとして出来ること以外に以下の事ができる。

  • initonly 属性を持つ static フィールドに書き込める

Type initializer の実行について、CLI によって保証されること

Type initializer の実行については、以下の点が守られることが、CLI 仕様によって保証されている(し、CLR*2 や mono はそれを守っている)。

共通して言えること

  • type initializer の実行タイミングについて
    • "all-zero" な値型や、null な参照型のメンバアクセスについてのみは、無視されうる
      • つまり、null なインスタンスのメンバにアクセスした場合などは、事前に type initializer が実行されるとは限らない
  • static field はいかなるアクセスよりも前に、known state に初期化されている
    • "all-zero" 値や null 値などであるということ

beforefieldinit 属性がないなら

  • 以下のうちいずれかの時に type initializer が実行される
    • その型の static field への初回アクセス時 または
    • その型の static method の初回の呼び出し時 または
    • その型のコンストラクタの初回の呼び出し時
  • 確実に一回だけ*3、与えられた型について実行される
    • ただし、ユーザーによって明示的に呼び出さた場合はこれは保証されない。
  • Type initializer の実行が完了していない型のフィールドには、type initializer によって呼ばれたメソッド以外はアクセスできない

beforefieldinit 属性でマークされているなら

  • 以下の時に type initializer が実行される
    • その型の static field への初回アクセス時 か それ以前

なお、beforefieldinit 属性がある場合、beforefieldinit ない場合で述べた事柄は保証されなくて良い。特に、最後の保証(type initializer の完了を待機する)が保証されないことがありうる。(ECMA-335 の 10.5.3.2 の Rationale 曰く、最後の保証は、複数 AppDomain 環境ではコストが高くつくうえ、滅多に必要がない*4ため、とのこと。)

また、この beforefieldinit の挙動は、初期化コードが特に有意な side-effect を持たない時のために用意されているそうだ(ECMA-335 8.9.5 の Note より)。

C# コンパイラの生成するコード

なお、C# コンパイラは、以下の処理を順番にひっつけて、Type initializer を生成する。

  1. static メンバの初期化処理
  2. static コンストラクタの中身

また、static コンストラクタが無い場合には、type initializer に beforefieldinit 属性を付加する。

*1:CLI 仕様(ECMA-335) 10.5.3 より、"a special method called a type initializer, which is used to initialize the type itself."

*2:.net framework で使われている CLI 実装

*3:executed exactly once

*4:多くのコードでは、type initializer は単にフィールドを初期化するためだけのものであることが理由だそうだ。