Переглянувши веб-семінар 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лише в конструкторі за замовчуванням.
Для ілюстрації другого випадку, наступний код:
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 .