TypeInitializationException の原因と直し方
静的コンストラクタ(staticフィールドの初期化を含む)の中で例外が起きると、直接の例外ではなくTypeInitializationExceptionに包まれて報告されます。
エラーメッセージの読み方
Unhandled exception. System.TypeInitializationException: The type initializer for 'Settings' threw an exception.
System- 名前空間 — System なら標準の基本例外です
TypeInitializationException- 例外クラス — 何が起きたか。ここを検索するのが最短です
The type initializer for 'Settings' threw an exception.- 詳細メッセージ — どの値が問題だったか
at Settings..cctor() in Program.cs:line 5- 発生箇所 — スタックトレースの先頭行
このエラーが出る典型パターン
パターン1
1 class Settings { 2 public static int Value; 3 static Settings() { 4 int denom = 0; ^ 5 Value = 100 / denom; 6 } 7 } 8 class Program { 9 static void Main(string[] args) { 10 System.Console.WriteLine(Settings.Value); 11 } 12 }
静的コンストラクタの中で例外が起きると、直接の例外ではなくTypeInitializationExceptionに包まれて報告されます。原因は内側のInnerExceptionにあります。
直し方: 0 を 2 にします。
パターン2
1 class Catalog { 2 public static string FirstItem; 3 static Catalog() { 4 string[] items = { "pen", "cup" }; 5 FirstItem = items[5 ]; ^ 6 } 7 } 8 class Program { 9 static void Main(string[] args) { 10 System.Console.WriteLine(Catalog.FirstItem); 11 } 12 }
IndexOutOfRangeExceptionのような別の例外でも、静的コンストラクタ内で起きた時点でTypeInitializationExceptionに包まれます。
直し方: 5 を 0 にします。
パターン3
1 class Parser { 2 public static int Parsed; 3 static Parser() { 4 Parsed = int.Parse("N/A"); ^ 5 } 6 } 7 class Program { 8 static void Main(string[] args) { 9 System.Console.WriteLine(Parser.Parsed); 10 } 11 }
FormatExceptionが原因でも同じです。静的コンストラクタの中で投げられた例外は種類を問わずTypeInitializationExceptionに包まれます。
直し方: "N/A" を "42" にします。
パターン4
1 class Cache { 2 public static int Total; 3 static Cache() { 4 int[] data = { 1, 2, 3 }; 5 Total = data[9 ]; ^ 6 } 7 } 8 class Program { 9 static void Main(string[] args) { 10 System.Console.WriteLine(Cache.Total); 11 } 12 }
配列の範囲外アクセスが原因でも、静的コンストラクタ内であればTypeInitializationExceptionに包まれます。
直し方: 9 を 1 にします。
パターン5
1 class Loader { 2 public static int Code; 3 static Loader() { 4 Code = int.Parse("xx" ); ^ 5 } 6 } 7 class Program { 8 static void Main(string[] args) { 9 System.Console.WriteLine(Loader.Code); 10 } 11 }
FormatExceptionが原因でも、静的コンストラクタの中で起きればTypeInitializationExceptionに包まれます。
直し方: "xx" を "7" にします。
よくある誤解
スタックトレースの一番外側だけを見るとTypeInitializationExceptionが原因に見えますが、本当の原因は「--->」で示される内側の例外(InnerException)にあります。
実務での勘所
静的コンストラクタは、その型が実際に初めて使われた瞬間に一度だけ実行される、という遅延初期化の仕組みで動いています(Javaのstatic初期化ブロックと同じ考え方です)。この一度きりの初期化のどこで例外が起きても、呼び出し元(その型を使おうとした、静的コンストラクタの定義とは全く違う場所のコード)が一貫した形で例外を受け取れるように、CLRはどんな例外が飛んできてもTypeInitializationExceptionという共通の型で包み直して報告します。本当の原因はInnerExceptionの中に保存されているという設計です。