これは決して信頼できる答えではありませんが、知識に基づいた推測をさせてください。
根本的な違いがあり、他の質問への回答はこの違いに関連していると思います。
特に継承のコンテキストでは、型の初期化の順序にあります。
では、インスタンスの初期化はどのように機能するのでしょうか?
C# の場合:
ctor が相互に呼び出したり、(明示的に) 基本クラスの ctor を呼び出したりする可能性は、状況を変えないので、省略します。
基本的に何が起こるかというと、これは最も派生したものから始めて、チェーン内の各 chass に対して実行されます。
Derived.initialize(){
derivedInstance.field1 = field1Initializer();
[...]
Base.Initialize();
Derived.Ctor();
}
簡単な例でこれを示します。
void Main()
{
new C();
}
class C: B {
public int c = GetInt("C.c");
public C(){
WriteLine("C.ctor");
}
}
class B {
public int b = GetInt("B.b");
public static int GetInt(string _var){
WriteLine(_var);
return 6;
}
public B(){
WriteLine("B.ctor");
}
public static void WriteLine(string s){
Console.WriteLine(s);
}
}
出力:
C.c
B.b
B.ctor
C.ctor
つまり、フィールド初期化子のフィールドへのアクセスが有効である場合、次のような惨事が発生する可能性があります。
class C: B {
int c = b; //b is a field inherited from the base class, and NOT YET INITIALIZED!
[...]
}
Java の場合:
型の初期化に関する長くて興味深い記事はこちらです。要約する:
インスタンスフィールド初期化子の概念に加えて、(オプションの)インスタンス初期化子の概念があるため、もう少し複雑ですが、その要点は次のとおりです。
すべてが継承チェーンを実行します。
証拠は次のとおりです: (またはオンラインで自分で実行します)
class Main
{
public static void main (String[] args) throws java.lang.Exception
{
new C();
}
}
class C extends B {
{
WriteLine("init C");
}
int c = GetInt("C.c");
public C(){
WriteLine("C.ctor");
}
}
class B {
{
WriteLine("init B");
}
int b = GetInt("B.b");
public static int GetInt(String _var){
WriteLine(_var);
return 6;
}
public B(){
WriteLine("B.ctor");
}
public static void WriteLine(String s){
System.out.println(s);
}
}
出力:
init B
B.b
B.ctor
init C
C.c
C.ctor
これが意味することは、フィールド初期化子が実行されるまでに、継承されたすべてのフィールドが (基本クラスの初期化子または ctor によって) 既に初期化されているため、この動作を許可するのに十分安全です。
class C: B {
int c = b; //b is inherited from the base class, and it's already initialized!
[...]
}
Java では、C# と同様に、フィールド初期化子は宣言順に実行されます。
Java コンパイラは、フィールド初期化子が out-of-order* と呼ばれていないことを確認する努力さえします。
class C {
int a = b; //compiler error: illegal forward reference
int b = 5;
}
* 余談ですが、イニシャライザがインスタンス メソッドを呼び出すと、順不同でフィールドにアクセスできます。
class C {
public int a = useB(); //after initializer completes, a == 0
int b = 5;
int useB(){
return b; //use b regardless if it was initialized or not.
}
}