派生クラスの静的コンストラクタの呼び出しタイミング

ハマりかけたのでメモ。
Visual Studio 2019を使ってC#で確認しました。


問題になったコードを極限まで単純化したのが以下のコードです。

using System;

namespace StaticConstructorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(TestClass2.n);
        }
    }

    public class TestClass1
    {
        public static int n = 1;

        static TestClass1()
        {
            n = 2;
        }
    }

    public class TestClass2 : TestClass1
    {
        static TestClass2()
        {
            n = 3;
        }
    }
}

直感的には出力は以下のようになると思っていました。

3

けれども実際の出力は以下でした。TestClass2の静的コンストラクタが呼ばれていません。

2

調べてみたところ、以下の記事を見つけました。

A static constructor is called automatically. It initializes the class before the first instance is created or any static members declared in that class (not its base classes) are referenced. A static constructor runs before an instance constructor. If static field variable initializers are present in the class of the static constructor, they're executed in the textual order in which they appear in the class declaration. The initializers run immediately prior to the execution of the static constructor.
(静的コンストラクタは自動的に呼ばれます。これは、最初のインスタンスが生成されるか、(基底クラスではなく)そのクラスで宣言されたいずれかの静的メンバーが参照される前に、そのクラスを初期化します。静的コンストラクタはインスタンスコンストラクタより前に動きます。もし静的フィールド変数のイニシャライザが静的コンストラクタのクラスに存在すれば、それらはクラス宣言に現れる順で実行されます。イニシャライザは静的コンストラクタの実行の直前に動きます。)

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

今回の問題は上記の「基底クラスではなく」の部分に引っかかっていたようです。コンソールにはTestClass2.nを出力していましたが、nはTestClass1のメンバーだったためTestClass2の静的コンストラクタが呼ばれていませんでした。知らんがな、こんな細かいルール。


そのため、以下のようにコードを修正すると

using System;

namespace StaticConstructorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(TestClass2.k);    // 追加
            Console.WriteLine(TestClass2.n);
        }
    }

    public class TestClass1
    {
        public static int n = 1;

        static TestClass1()
        {
            n = 2;
        }
    }

    public class TestClass2 : TestClass1
    {
        public static int k = 5;    // 追加

        static TestClass2()
        {
            n = 3;
        }
    }
}

出力は以下のようになりました。

5
3

この記事が気に入ったらサポートをしてみませんか?