Après avoir regardé le webinaire Jon Skeet inspecte ReSharper J'ai commencé à jouer un peu avec et j'ai trouvé que le code suivant est du code C# valide (par valide, je veux dire qu'il compile).
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() { }
}
Comme nous le savons tous probablement, l'initialisation des champs est déplacée dans le constructeur par le compilateur. Donc si vous avez un champ comme int a = 42;
vous aurez a = 42
sur todo constructeurs. Mais si vous avez un constructeur qui appelle un autre constructeur, vous aurez du code d'initialisation uniquement dans le constructeur appelé.
Par exemple, si vous avez un constructeur avec des paramètres appelant le constructeur par défaut, vous aurez une affectation a = 42
uniquement dans le constructeur par défaut.
Pour illustrer le deuxième cas, le code suivant :
class Foo
{
int a = 42;
Foo() :this(60) { }
Foo(int v) { }
}
Se compile en :
internal class Foo
{
private int a;
private Foo()
{
this.ctor(60);
}
private Foo(int v)
{
this.a = 42;
base.ctor();
}
}
Le problème principal est donc que mon code, donné au début de cette question, est compilé en :
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();
}
}
Comme vous pouvez le voir, le compilateur ne peut pas décider où placer l'initialisation des champs et, par conséquent, ne la place nulle part. Notez également qu'il n'y a pas de base
appels au constructeur. Bien sûr, aucun objet ne peut être créé, et vous vous retrouverez toujours avec StackOverflowException
si vous essayez de créer une instance de Foo
.
J'ai deux questions :
Pourquoi le compilateur autorise-t-il les appels récursifs aux constructeurs ?
Pourquoi observons-nous un tel comportement du compilateur pour les champs, initialisés dans une telle classe ?
Quelques notes : ReSharper vous avertit avec Possible cyclic constructor calls
. De plus, en Java, de tels appels de constructeurs ne seront pas compilés, le compilateur Java est donc plus restrictif dans ce scénario (Jon a mentionné cette information lors du webinaire).
Cela rend ces questions plus intéressantes, car avec tout le respect dû à la communauté Java, le compilateur C# est au moins plus moderne.
Il a été compilé en utilisant C# 4.0 y C# 5.0 et décompilés à l'aide de dotPeek .