ウェビナーのJonSkeet Inspects ReSharperを見た後、再帰的なコンストラクター呼び出しを少し試し始めたところ、次のコードが有効なC#コードであることがわかりました(有効とはコンパイルを意味します)。
class Foo
{
int a = null;
int b = AppDomain.CurrentDomain;
int c = "string to int";
int d = NonExistingMethod();
int e = Invalid<Method>Name<<Indeeed();
Foo() :this(0) { }
Foo(int v) :this() { }
}
ご存知かもしれませんが、フィールドの初期化はコンパイラーによってコンストラクターに移されます。したがって、のようなフィールドがある場合int a = 42;はa = 42、すべてのコンストラクターにあります。ただし、別のコンストラクターを呼び出すコンストラクターがある場合は、呼び出されたコンストラクターにのみ初期化コードがあります。
たとえば、デフォルトコンストラクターを呼び出すパラメーターを持つコンストラクターがある場合a = 42、デフォルトコンストラクターにのみ割り当てがあります。
2番目のケースを説明するために、次のコードは次のとおりです。
class Foo
{
int a = 42;
Foo() :this(60) { }
Foo(int v) { }
}
コンパイル先:
internal class Foo
{
private int a;
private Foo()
{
this.ctor(60);
}
private Foo(int v)
{
this.a = 42;
base.ctor();
}
}
したがって、主な問題は、この質問の冒頭で示した私のコードが次のようにコンパイルされていることです。
internal class Foo
{
private int a;
private int b;
private int c;
private int d;
private int e;
private Foo()
{
this.ctor(0);
}
private Foo(int v)
{
this.ctor();
}
}
ご覧のとおり、コンパイラはフィールドの初期化をどこに配置するかを決定できず、その結果、どこにも配置しません。また、baseコンストラクター呼び出しがないことにも注意してください。もちろん、オブジェクトを作成することはできません。のStackOverflowExceptionインスタンスを作成しようとすると、常に最終的になりますFoo。
2つの質問があります:
コンパイラが再帰的なコンストラクタ呼び出しを許可するのはなぜですか?
そのようなクラス内で初期化されたフィールドに対するコンパイラのそのような動作を観察するのはなぜですか?
いくつかの注意:ReSharperはで警告しますPossible cyclic constructor calls。さらに、Javaでは、このようなコンストラクター呼び出しはイベントコンパイルを行わないため、このシナリオではJavaコンパイラーがより制限されます(Jonはウェビナーでこの情報について言及しました)。
これにより、これらの質問がより興味深いものになります。Javaコミュニティに関しては、C#コンパイラは少なくとも最新のものだからです。