JavaとC#で、フィールドの初期化順序が違う (JavaプログラマーがC#でプログラムを書いて引っかかったところ その5)

C# の本を読んでいたら、クラスのフィールドの初期化順序が Java と違っていてびっくりしました。

Java のフィールド初期化順序

Javaインスタンスを作成した場合、親クラスのフィールド初期化 → 親クラスのコンストラクタ実行 → 子クラスのフィールド初期化 → 子クラスのコンストラクタ実行 の順番で処理が行われます。

そのため、下記では Parent クラスのコンストラクタを呼び出した時点では Child クラスの c フィールドは初期化されていないため、System.out.println での出力が初期値である null になっています。

public class Program {
    public static void main(String[] args) {
        new Child();
    }

    public static class Parent {
        // 順序① - 親クラスのフィールド初期化
        protected String p = "parent";

        // 順序② - 親クラスのコンストラクタ実行
        public Parent() {
            // 下記の実行結果は "parent, null"
            System.out.println(this.toString());
        }
    }

    public static class Child extends Parent
    {
        // 順序③ - 子クラスのフィールド初期化
        protected String c = "child";

        // 順序④ - 子クラスのコンストラクタ実行
        public Child() {
            // 下記の実行結果は "parent, child"
            System.out.println(this.toString());
        }

        @Override
        public String toString() {
            return String.format("%s, %s", p, c);
        }
    }
}

C# のフィールド初期化順序

一方の C# では、インスタンスを作成した場合、子クラスのフィールド初期化 → 親クラスのフィールド初期化 → 親クラスのコンストラクタ実行 → 子クラスのコンストラクタ実行 の順番で処理が行われます。

そのため、下記では Parent クラスのコンストラクタを呼び出した時点で Child クラスの c フィールドが初期化されているため、Console.WriteLine の出力が "child" になっています。

using System;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            new Child();
        }
    }

    public class Parent
    {
        // 順序② - 親クラスのフィールド初期化
        protected string p = "parent";

        // 順序③ - 親クラスのコンストラクタ実行
        public Parent()
        {
            // 下記の実行結果は "parent, child"
            Console.WriteLine(this.ToString());
        }
    }

    public class Child : Parent
    {
        // 順序① - 子クラスのフィールド初期化
        protected string c = "child";

        // 順序④ - 子クラスのコンストラクタ実行
        public Child()
        {
            // 下記の実行結果は "parent, child"
            Console.WriteLine(this.ToString());
        }

        public override string ToString()
        {
            return string.Format("{0}, {1}", p, c);
        }
    }
}


こんな違いがあるとは知りませんでした!