C#のモジュール初期化子は
「モジュールが読み込まれたタイミングで呼ばれるメソッド」
という表現は正しいのだけれど、
「モジュールが読み込まれたタイミングでまず最初に呼ばれるメソッド」
という表現は適切ではなさそう。
「モジュール初期化子は、モジュールが読み込まれたタイミングで呼ばれる。
ただし、その前に呼ぶ必要があるメソッドを、すべて呼び出した後に呼ばれる点に注意が必要。」
くらいの表現が、適切な気がする。
具体的には、モジュール初期化子が呼び出される前に、そのクラスの静的コンストラクターがあれば、呼び出されるはず。また、その静的コンストラクターの呼び出しに必要な処理やメソッドがあれば、それも呼ばれるはず。
具体的なコード例とその実行結果を示す。
コメント中の数字は実行される順番。
これは極端な例であることに注意。
using System.Runtime.CompilerServices;
public class Program
{
public static void Main(string[] args)
{
// 12. 最後に、「Hello World!」と表示
Console.WriteLine("Hello World!");
}
}
public class ModuleInitializerCaller
{
// 1. ModuleInitializerCallerの静的フィールドの初期化を行おうとする。
// そのためにAnotherClassの初期化が必要
// 6. その後、AnotherClassのインスタンスを生成する
static AnotherClass _anotherClassInstance = new AnotherClass("in ModuleInitializerCaller");
// 8. ModuleInitializerCallerの静的フィールドの初期化を行おうとする。
// そのために、ModuleInitializerCallerのインスタンスを生成する
static ModuleInitializerCaller _instance = new ModuleInitializerCaller();
// 10. ModuleInitializerCallerの静的コンストラクターが呼ばれる
// 「static ModuleInitializerCaller」と表示
static ModuleInitializerCaller()
{
Console.WriteLine("static ModuleInitializerCaller");
}
// 0. ModuleInitializer属性がついているから、モジュールが読み込まれたタイミングでModuleInitializerCaller#InitializeModuleを呼び出そうとする。
// その前に、必要なメソッドや処理を呼び出す。
// 11. 必要なメソッドや処理の呼び出しが終わったので、InitializeModuleメソッドを呼び出し
// 「ModuleInitializerCaller#InitializeModule」と表示
[ModuleInitializer]
public static void InitializeModule()
{
Console.WriteLine("ModuleInitializerCaller#InitializeModule");
}
// 9. ModuleInitializerCallerの静的フィールドの初期化のために、ModuleInitializerCallerのコンストラクターが呼ばれる
// 「constructor ModuleInitializerCaller」と表示
ModuleInitializerCaller()
{
Console.WriteLine("constructor ModuleInitializerCaller");
}
}
// 2. AnotherClassの初期化をする
public class AnotherClass
{
// 3. AnotherClassの静的フィールドの初期化を行おうとする。
// そのためにAnotherClassのインスタンスを生成する
static AnotherClass _instance = new AnotherClass("in AnotherClass");
// 5. AnotherClassの静的コンストラクターが呼ばれる
// 「static AnotherClass」と表示
static AnotherClass()
{
Console.WriteLine("static AnotherClass");
}
// 4. AnotherClassの静的フィールドの初期化のために、AnotherClassのコンストラクターが呼ばれる
// 「constructor AnotherClass AnotherClass」と表示
// 7. ModuleInitializerCallerの静的フィールドの初期化のために、AnotherClassのコンストラクターが呼ばれる
// 「constructor AnotherClass ModuleInitializerCaller」と表示
public AnotherClass(string message)
{
Console.WriteLine($"constructor AnotherClass {message}");
}
}
上記のコードを「.NET 10.0.100」で実行した結果は、次の通り。
constructor AnotherClass in AnotherClass
static AnotherClass
constructor AnotherClass in ModuleInitializerCaller
constructor ModuleInitializerCaller
static ModuleInitializerCaller
ModuleInitializerCaller#InitializeModule
Hello World!
処理は次のような順番で行われる。
-
ModuleInitializer属性がついているから、モジュールが読み込まれたタイミングでModuleInitializerCaller#InitializeModuleを呼び出そうとする。
その前に、必要なメソッドや処理を呼び出す。 -
ModuleInitializerCallerの静的フィールドの初期化を行おうとする。そのためにAnotherClassの初期化が必要。
-
AnotherClassの初期化をする
-
AnotherClassの静的フィールドの初期化を行おうとする。そのためにAnotherClassのインスタンスを生成する。
-
AnotherClassの静的フィールドの初期化のために、AnotherClassのコンストラクターが呼ばれる
「constructor AnotherClass in AnotherClass」と表示 -
AnotherClassの静的コンストラクターが呼ばれる
「static AnotherClass」と表示 -
その後、AnotherClassのインスタンスを生成する
-
ModuleInitializerCallerの静的フィールドの初期化のために、AnotherClassのコンストラクターが呼ばれる
「constructor AnotherClass in ModuleInitializerCaller」と表示 -
ModuleInitializerCallerの静的フィールドの初期化を行おうとする。
そのために、ModuleInitializerCallerのインスタンスを生成する -
ModuleInitializerCallerの静的フィールドの初期化のために、ModuleInitializerCallerのコンストラクターが呼ばれる
「constructor ModuleInitializerCaller」と表示 -
ModuleInitializerCallerの静的コンストラクターが呼ばれる
「static ModuleInitializerCaller」と表示 -
必要なメソッドや処理の呼び出しが終わったので、InitializeModuleメソッドを呼び出し
「ModuleInitializerCaller#InitializeModule」と表示 -
最後に、「Hello World!」と表示
実行順序を理解するために、抑えるべきポイントは次の通り。
- モジュール初期化子の前に、そのメソッドが定義されているクラスの静的コンストラクターが呼び出される
- 静的コンストラクターが呼び出される前に、静的フィールドの初期化が終わる
- 静的フィールドの初期化順序は定義順
というわけで、
モジュール初期化子は、モジュールが読み込まれたタイミングで呼ばれる。
ただし、その前に呼ぶ必要があるメソッドを、すべて呼び出した後に呼ばれる点に注意が必要。