Nachdem ich mir das Webinar Jon Skeet Inspects ReSharper angesehen hatte , begann ich ein wenig mit rekursiven Konstruktoraufrufen zu spielen und stellte fest, dass der folgende Code gültiger C # -Code ist (mit gültig meine ich, dass er kompiliert wird).
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() { }
}
Wie wir alle wahrscheinlich wissen, wird die Feldinitialisierung vom Compiler in den Konstruktor verschoben. Wenn Sie also ein Feld wie haben int a = 42;, haben Sie a = 42in allen Konstruktoren. Wenn Sie jedoch einen Konstruktor haben, der einen anderen Konstruktor aufruft, haben Sie Initialisierungscode nur in aufgerufenem.
Wenn Sie beispielsweise einen Konstruktor mit Parametern haben, die den Standardkonstruktor aufrufen, haben Sie die Zuweisung a = 42nur im Standardkonstruktor.
Um den zweiten Fall zu veranschaulichen, folgender Code:
class Foo
{
int a = 42;
Foo() :this(60) { }
Foo(int v) { }
}
Kompiliert in:
internal class Foo
{
private int a;
private Foo()
{
this.ctor(60);
}
private Foo(int v)
{
this.a = 42;
base.ctor();
}
}
Das Hauptproblem ist also, dass mein Code, der zu Beginn dieser Frage angegeben wurde, wie folgt kompiliert ist:
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();
}
}
Wie Sie sehen, kann der Compiler nicht entscheiden, wo die Feldinitialisierung abgelegt werden soll, und legt sie daher nirgendwo ab. Beachten Sie auch, dass es keine baseKonstruktoraufrufe gibt. Natürlich können keine Objekte erstellt werden, und Sie werden immer am Ende sein, StackOverflowExceptionwenn Sie versuchen, eine Instanz von zu erstellen Foo.
Ich habe zwei Fragen:
Warum erlaubt der Compiler überhaupt rekursive Konstruktoraufrufe?
Warum beobachten wir ein solches Verhalten des Compilers für Felder, die innerhalb einer solchen Klasse initialisiert wurden?
Einige Hinweise: ReSharper warnt Sie mit Possible cyclic constructor calls. Darüber hinaus werden solche Konstruktoraufrufe in Java nicht als Ereignis kompiliert, sodass der Java-Compiler in diesem Szenario restriktiver ist (Jon erwähnte diese Informationen im Webinar).
Dies macht diese Fragen interessanter, da der C # -Compiler in Bezug auf die Java-Community zumindest moderner ist.
Dies wurde mit C # 4.0- und C # 5.0- Compilern kompiliert und mit dotPeek dekompiliert .
int a = null; int b = AppDomain.CurrentDomain; int c = "string to int"; int d = NonExistingMethod(); int e = Invalid<Method>Name<<Indeeed();Man sollte ein Quiz machen: "In welcher Situation sind diese Felddeklarationen in Ordnung?" (Es gibt eine Warnung, dass die Felder nicht verwendet werden, aber Sie können diese Warnung entfernen, indem Sie jedes Feld im Körper eines der Intance-Konstruktoren (oder anderswo) lesen.)