Help us understand the problem. What is going on with this article?

[.NET] コンストラクタより先にメソッドが実行されてしまうからくり――を解き明かすために派生クラスのインスタンス生成過程を追跡する

More than 1 year has passed since last update.

.NET では、コンストラクタから仮想メソッドを呼び出すと、派生型コンストラクタが処理されていない状態でオーバーライドメソッドが実行されてしまいます。

例として、基底クラス Base と 派生クラス Derived があるとします。
(括弧付き数字は実行される順番です)

// 基底クラス
class Base
{
    private static bool baseStaticFieldInitialized = ConsoleOut("(1) 基底型静的フィールドの初期化");

    private bool baseInstanceFieldInitialized = ConsoleOut("(4) 基底型インスタンスフィールドの初期化");

    public Base()
    {
        ConsoleOut("(5) 基底型コンストラクタの実行");

        // ※派生型コンストラクタはまだ実行されていないが、この時点でもインスタンスは派生型
        Assert.AreEqual(typeof(Derived), this.GetType());

        // ※オーバーライドされた派生型メソッドが実行される。
        OverridableMethod();
    }

    // 仮想メソッド
    protected virtual void OverridableMethod()
    {
        // ※インスタンス型でオーバーライドされている場合、コンストラクタからの呼び出しでも実行されない。
        Assert.Fail();
    }

    protected static bool ConsoleOut(string step)
    {
        Console.WriteLine(step);
        return true;
    }
}

// 派生クラス
sealed class Derived : Base
{
    private static bool derivedStaticFieldInitialized = ConsoleOut("(2) 派生型静的フィールドの初期化");

    // ※基底型インスタンスフィールド、基底型コンストラクタより先に処理される。
    private bool derivedInstanceFieldInitialized = ConsoleOut("(3) 派生型インスタンスフィールドの初期化");

    private bool derivedConstructorCalled = false;

    public Derived()
    {
        ConsoleOut("(7) 派生型コンストラクタの実行");

        this.derivedConstructorCalled = true;
    }

    // オーバーライドメソッド
    protected override void OverridableMethod()
    {
        ConsoleOut("(6) 派生型オーバーライドメソッドの実行");

        // ※基底型コンストラクタから呼び出された場合、
        //  派生型フィールドは初期化済みだが、派生型コンストラクタが未処理のまま実行されてしまう。
        Assert.IsTrue(this.derivedInstanceFieldInitialized);
        Assert.IsFalse(this.derivedConstructorCalled);
    }
}

実行結果は以下のようになります。

new Derived();
(1) 基底型静的フィールドの初期化
(2) 派生型静的フィールドの初期化
(3) 派生型インスタンスフィールドの初期化
(4) 基底型インスタンスフィールドの初期化
(5) 基底型コンストラクタの実行
(6) 派生型オーバーライドメソッドの実行
(7) 派生型コンストラクタの実行

なお、非 sealed 型コンストラクタからの仮想メソッドを呼び出しは、コード分析 FxCop(弊社支援サービス参考)で、「CA2214: コンストラクターのオーバーライド可能なメソッドを呼び出しません。」として警告されます。

CodeOne
【品質と生産性にこだわるシステム開発】 .NET(C#/VB.NET)専門・リモート開発歴10年。即日・1時間から頼める常駐しないエンジニア。確かな技術で開発チームを手堅くサポートします。
https://codeone.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした