6

私が知っているように、親クラスのコンストラクターが最初に呼び出され、次に子クラスが呼び出されますが、静的コンストラクターの場合、最初に派生クラス、次に子クラスから実行されるのはなぜですか?

namespace ConsoleApplication1
    
    {
   
 class Program

    {

        static void Main(string[] args)
        {
            Child t = new Child();
        }
    }

    class Parent
    {
        public  Parent()
        {
            Console.WriteLine("Parent Instance Constructor");
            Console.ReadKey();
        }

        static Parent()
        {
            Console.WriteLine("Parent Static Constructor");
            Console.ReadKey();
        }
    }
    class Child : Parent
    {
        public Child()
        {
            Console.WriteLine("Child Instance Constructor");
            Console.ReadKey();
        }

        static Child()
        {
            Console.WriteLine("Child Static Constructor");
            Console.ReadKey();
        }
    }
}

出力:

子静的コンストラクタ

親静的コンストラクタ

親インスタンスコンストラクタ

子インスタンスコンストラクタ

Jeppe Stig Nielsenの提案によると、コンストラクターで静的フィールドを初期化した場合、次の順序で実行されます。

出力

親静的コンストラクタ

子静的コンストラクタ

親インスタンスコンストラクタ

子インスタンスコンストラクタ

class XyzParent
{
    protected static int FieldOne;
    protected int FieldTwo;

    static XyzParent()
    {
        // !  
        FieldOne = 1;
        Console.WriteLine("parent static");
    }
    internal XyzParent()
    {
        // !  
        FieldOne = 10;
        // !  
        FieldTwo = 20;
        Console.WriteLine("parent instance");
    }
}
class XyzChild : XyzParent
{
    static XyzChild()
    {
        // !  
        FieldOne = 100;
        Console.WriteLine("child static");
    }
    internal XyzChild()
    {
        // !  
        FieldOne = 1000;
        // !  
        FieldTwo = 2000;
        Console.WriteLine("child instance");
    }
}

なぜそのような矛盾した行動?

4

3 に答える 3

24

まず、動作はまったく矛盾していません。それはすべて規則と一致しています。あなたはただルールが何であるかを知らない。

インスタンスコンストラクターに関する私の2部構成のシリーズと、静的コンストラクターのセマンティクスに関する私の4部構成のシリーズのすべてを読む必要があります。彼らはここから始まります:

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

そしてここ:

http://ericlippert.com/2013/02/06/static-constructors-part-one/

それぞれ。

それらはあなたの質問に明確に答えるべきですが、それが100%明確でない場合は、要約させてください。関連するルールは次のとおりです。

  • ルール1:静的コンストラクターは、静的フィールドにアクセスする前、静的メソッドが実行される前、およびインスタンスコンストラクターが実行される前に実行されます。
  • ルール2:派生クラスインスタンスコンストラクターは、派生クラスインスタンスコンストラクター本体を実行する前に、基本クラスインスタンスコンストラクターを呼び出します。

では、実行するとどうなりますnew Child()か?

  • ルール1が適用されます。Childのインスタンスコンストラクターを呼び出そうとしているので、最初にChildの静的コンストラクターを呼び出す必要があります。したがって、最初に実行されます。
  • Childの静的コンストラクターが戻った後、Childのインスタンスコンストラクターが実行されます。ルール2が適用されます。子インスタンスコンストラクターが本体を実行する前に最初に行うことは、親のインスタンスコンストラクターを実行することです。
  • ルール1が再度適用されます。Parentのインスタンスコンストラクターを呼び出そうとしているので、最初にParentの静的コンストラクターを呼び出す必要があります。だからそれは実行されます。
  • Parentの静的コンストラクターが戻った後、Parentのインスタンスコンストラクターが実行されます。ルール2が適用されます。オブジェクトのインスタンスコンストラクターを呼び出しますが、これは何も興味深いことを行わず、Parentのインスタンスコンストラクターの本体を実行します。
  • ControlはChildのインスタンスコンストラクターに戻り、その本体が実行されます。

さあ、行きましょう。順序は、子静的コンストラクター、次に親静的コンストラクター、次に親本体、次に子本体です。

次に、2番目の例を見てみましょう。あなたが言うとどうなりますnew XyzChildか?

  • ルール1が適用されます。XyzChildのインスタンスコンストラクターを呼び出そうとしているので、最初にXyzChildの静的コンストラクターを呼び出します。本体が実行を開始し、...
  • ...ルール1が再度適用されます。XyzParentの静的フィールドにアクセスしようとしているため、XyzParentの静的コンストラクターを実行する必要があります。
  • XyzParentの静的コンストラクターが実行されます。フィールドにアクセスしますが、静的コンストラクターはすでにこのスレッドで実行されているため、静的コンストラクターを再度再帰的にトリガーすることはありません。親にあることを印刷します。
  • 制御は子の静的コンストラクターに戻り、子の静的コンストラクターが子にあることを出力します。
  • これで、子のインスタンスコンストラクターを実行できます。ルール2が適用されます。XyzParentのインスタンスコンストラクターが最初に実行されます。
  • ルール1が適用されますが、XyzParentの静的コンストラクターはすでに実行されているため、スキップされます。
  • XyzParentのインスタンスコンストラクターの本体が実行され、制御がXyzChildの静的コンストラクターに返されます。
  • XyzChildのインスタンスコンストラクターの本体が実行されます。

さあ、行きます。矛盾はまったくありません。2つのルールが正しく適用されます。

于 2013-03-17T20:16:44.700 に答える
6

Staticコンストラクターは、常に非静的コンストラクターの前に実行されます。静的コンストラクターは、クラスが最初にアクセスされるときに呼び出されます。

MSDN Docから、

  • 静的コンストラクターは、アクセス修飾子を受け取らないか、パラメーターを持ちません。
  • 静的コンストラクターは、最初のインスタンスが作成される前、または静的メンバーが参照される前に、クラスを初期化するために自動的に呼び出されます。
  • 静的コンストラクターを直接呼び出すことはできません。ユーザーは、静的コンストラクターがプログラムでいつ実行されるかを制御できません。
  • 静的コンストラクターの一般的な使用法は、クラスがログファイルを使用していて、コンストラクターを使用してこのファイルにエントリを書き込む場合です。
  • 静的コンストラクターは、コンストラクターがLoadLibraryメソッドを呼び出すことができる場合に、アンマネージコードのラッパークラスを作成するときにも役立ちます。
  • 静的コンストラクターが例外をスローした場合、ランタイムはそれを2回呼び出すことはなく、プログラムが実行されているアプリケーションドメインの存続​​期間中、型は初期化されないままになります。
于 2013-03-17T14:58:36.043 に答える
2

あなたの場合、静的コンストラクターが実行される順序は未定義です(私は思います)。保証される唯一のことは、インスタンスが作成される前に実行されることです。

私はあなたのコードを次のように変更しました:

    class XyzParent
    {
        protected static int FieldOne;
        protected int FieldTwo;

        static XyzParent()
        {
            FieldOne = 1;
            Console.WriteLine("parent static");
        }
        internal XyzParent()
        {
            FieldOne = 10;
            FieldTwo = 20;
            Console.WriteLine("parent instance");
        }
    }
    class XyzChild : XyzParent
    {
        static XyzChild()
        {
            FieldOne = 100;
            Console.WriteLine("child static");
        }
        internal XyzChild()
        {
            FieldOne = 1000;
            FieldTwo = 2000;
            Console.WriteLine("child instance");
        }
    }

同じフィールドに書き込むため、実行する順序がより重要になります。そして、私のバージョンのコードでは、次のように言っnew XyzChild();てこの出力につながります。

parent static
child static
parent instance
child instance

編集:エリックリッパートの答えは、より正確な説明を与えます。上記のコードは、静的コンストラクターWriteLine最後でのみ実行されます。静的コンストラクターの先頭に追加WriteLineして、静的コンストラクターが静的コンストラクターの実行の「途中」で実行されることを確認します。XyzParentXyzChild

于 2013-03-17T15:23:58.347 に答える