После просмотра веб-семинара Джон Скит инспектирует 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только в конструкторе по умолчанию.
Чтобы проиллюстрировать второй случай, следующий код:
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 компилятор C #, по крайней мере, более современный.
Он был скомпилирован с использованием компиляторов C # 4.0 и C # 5.0 и декомпилирован с помощью dotPeek .