После просмотра веб-семинара Jon Skeet Inspects ReSharper, я начал немного играть с рекурсивный вызов конструктора и нашел, что следующий код является допустимым кодом С# (по-моему, это означает, что он компилируется).
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
в конструкторах all. Но если у вас есть конструктор, вызывающий другой конструктор, у вас будет код инициализации только в вызываемом.
Например, если у вас есть конструктор с параметрами, вызывающими конструктор по умолчанию, у вас будет назначение a = 42
только в конструкторе по умолчанию.
Чтобы проиллюстрировать второй случай, следующий код:
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
.
У меня есть два вопроса:
Почему компилятор разрешает рекурсивные вызовы конструктора вообще?
Почему мы наблюдаем такое поведение компилятора для полей, инициализированных в этом классе?
Некоторые примечания: ReSharper предупреждает вас с помощью Possible cyclic constructor calls
. Более того, в Java такие вызовы конструктора не будут компилировать события, поэтому компилятор Java более строгий в этом сценарии (Джон упомянул эту информацию на веб-семинаре).
Это делает эти вопросы более интересными, потому что со всем уважением к сообществу Java компилятор С#, по крайней мере, более современен.
Это было скомпилировано с помощью С# 4.0 и С# 5.0 и декомпилировано с помощью dotPeek.