Po obejrzeniu webinaru Jon Skeet Inspects ReSharper , zacząłem trochę bawić się rekurencyjnymi wywołaniami konstruktora i stwierdziłem, że poniższy kod jest prawidłowym kodem C # (przez prawidłowy, mam na myśli, że kompiluje).
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() { }
}
Jak wszyscy zapewne wiemy, inicjalizacja pola jest przenoszona do konstruktora przez kompilator. Więc jeśli masz takie pole int a = 42;, będziesz mieć a = 42we wszystkich konstruktorach. Ale jeśli masz konstruktora wywołującego inny konstruktor, będziesz miał kod inicjujący tylko w wywołanym.
Na przykład, jeśli masz konstruktora z parametrami wywołującymi domyślny konstruktor, przypisanie będziesz mieć a = 42tylko w domyślnym konstruktorze.
Aby zilustrować drugi przypadek, następny kod:
class Foo
{
int a = 42;
Foo() :this(60) { }
Foo(int v) { }
}
Kompiluje do:
internal class Foo
{
private int a;
private Foo()
{
this.ctor(60);
}
private Foo(int v)
{
this.a = 42;
base.ctor();
}
}
Więc głównym problemem jest to, że mój kod, podany na początku tego pytania, jest wkompilowany w:
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();
}
}
Jak widać, kompilator nie może zdecydować, gdzie umieścić inicjalizację pola iw rezultacie nigdzie go nie umieszcza. Należy również zauważyć, że nie ma basewywołań konstruktora. Oczywiście nie można tworzyć żadnych obiektów i zawsze skończy się to, StackOverflowExceptionjeśli spróbujesz utworzyć instancję Foo.
Mam dwa pytania:
Dlaczego kompilator w ogóle zezwala na rekurencyjne wywołania konstruktora?
Dlaczego obserwujemy takie zachowanie kompilatora dla pól zainicjowanych w ramach takiej klasy?
Kilka uwag: ReSharper ostrzega Possible cyclic constructor calls. Co więcej, w Javie takie wywołania konstruktorów nie będą kompilować zdarzeń, więc kompilator Javy jest bardziej restrykcyjny w tym scenariuszu (Jon wspomniał o tej informacji na webinarium).
To sprawia, że te pytania są bardziej interesujące, ponieważ z całym szacunkiem dla społeczności Java, kompilator C # jest przynajmniej nowocześniejszy.
Zostało to skompilowane przy użyciu kompilatorów C # 4.0 i C # 5.0 i zdekompilowane przy użyciu dotPeek .