C#でTypeInitializationExceptionが発生する

ある日突然プログラムを実行したら「TypeInitializationException」が発生した場合の対処法です。

自分のコード内で発生している場合、以降で説明する状況で発生している可能性があります。

// 発生する例外のメッセージ

// .NET Core
System.TypeInitializationException: The typeinitializer for 
'{ClassName}' threw an exception. 

// .NET Framework (ja-jp)
System.TypeInitializationException: '{ClassName}' のタイプ初期化子が例外をスローしました。

発生する状況と発生する理由

例えば、以下の実装は一見問題なさそうな static フィールドを呼び出したように見えますが TypeInitializationException が発生します。

using System;

namespace ConsoleApp26
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                int a = StaticClassSample.B;
                //
                // アクセスしたとき or メソッドに入った時に以下の例外が発生する
                //
                // System.TypeInitializationException: 
                //   The type initializer for 'StaticClassSample' threw an exception. 
                //    ---> System.ArgumentException: Value does not fall within the expected range.
                //    at Sample.Int() in ****\Program.cs:line 36
                //    at StaticClassSample..cctor() in ****\Program.cs:line 31
                //    --- End of inner exception stack trace ---
                //    at Program.Main(String[] args) in ****\Program.cs:line 11
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }

    public static class StaticClassSample
    {
        public static int A = BadMethod(); // こういうフィールドアクセスは問題になる

        public static int B = NormalMethod();

        public static int C => BadMethod(); // プロパティの場合問題にならない

        // メソッド内で例外が起きる
        public static int BadMethod() => throw new ArgumentException();

        // 例外が起きない呼び出し
        public static int NormalMethod() => 10;

        StaticClassSample()
        {
            throw new Exception(); // こういう場合も問題になる
        }
    }
}

発生するメカニズムは以下の通りです。

(1) staticな要素への初回呼び出しが発生 ↓ (2) .NETのランタイム側で初回の自動的フィールド初期化で暗黙的にstaticコンストラクタが呼び出される ↓ (3) staticコンストラクタ実行中に例外が起きる ↓ (4) TypeInitializationExceptionが発生する

の順序で発生します。

この時、static クラスコンストラクタで処理が失敗したため TypeInitializationException (= 型の初期化の失敗) という例外が発生します。厄介なことに自分が直接参照してるクラスが別の static なフィールドを参照してそこで例外が発生してもこの状態となります。

ただし、補足した例外のスタックトレースが確認できる、例外メッセージに問題のある型の名前は出るので、当該クラスの static フィールドアクセスかつ、エラーが出る実装をしてそうな個所をまずは確認しましょう。

もし、外部ライブラリ側のクラスだった場合、ライブラリの使い方が間違ってないかを確認してそれでも解消しない場合は開発元に問い合わせるなど問題が大きくなる可能性もあります。

問題を起こさないために

このような問題を起こさないために、自分がする実装ではstaticコンストラクタやフィールドの初期化時に「定義ファイルを外部リソースから読んでstaticフィールドに格納する」とか、「外部通信を準備する、実際に通信する」「DBアクセスする」などの処理に失敗する可能性がある実装をしないようにしましょう。

そういった例外が発生しそうな初期化は別途メソッドに実装します。