ウェビナーJon Skeet 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# コンパイラは少なくともより最新であるためです。
これは、C# 4.0およびC# 5.0コンパイラを使用してコンパイルされ、 dotPeekを使用して逆コンパイルされました。