- Les structures en C# sont amusantes
- Brève introduction aux Value Types vs Reference Types.
- Initialisation des champs dans les structures (cet post).
- Comportement des constructeurs dans structures.
- Des autres scénarios dans lesquels le comportement des constructeurs de structure peut vous surprendre.
- Struct avec des valeurs d'argument par défaut dans les constructeurs, ou, n'êtes-vous pas encore confus ?
- Le modificateur `required`de C # 11 ne sauvegardera pas votre
c*lemploi. - Structure utilisée comme valeurs d'argument défaut.
- Bonus: L'evolution des structures en C#.
Poursuivant notre série sur les types de valeur (Value Types), étant donné le code ci-dessous, qu'attendez-vous qu'il imprime (je vous recommande d'essayer de répondre avant de l'exécuter):
var s2 = new S2(); | |
System.Console.WriteLine(s2.v); | |
s2.v = 1; | |
struct S2 | |
{ | |
public int v = 32; | |
public S2(int i) => v = i; | |
} |
Si vous avez répondu 0 (zéro), vous l'avez cloué et avez probablement déjà une bonne compréhension de la façon dont .NET/C# gère l'instanciation de type valeur/l'initialisation de champ; par contre, si vous avez répondu 32, vous avez peut-être été trompé.
Dans cet article, je vais aborder brièvement l'un des aspects qui conduisent à ce comportement : l'initialisation des champs en .NET, ou pour être plus précis, en C#.
D'une manière trop simpliste, chaque fois que le compilateur C# trouve une initialisation de champ, il déplacera simplement le code d'initialisation vers les constructeurs, c'est-à-dire que l'initialisation d'un champ équivaut à définir sa valeur dans les constructeurs (les champs statiques sont initialisés dans les constructeurs statiques), donc étant donné le code ci-dessous :
public class Foo | |
{ | |
private int f = 42; | |
public Foo() | |
{ | |
System.Console.WriteLine("Foo()"); | |
} | |
public Foo(int i) | |
{ | |
System.Console.WriteLine("Foo(int)"); | |
} | |
} |
le compilateur C# le traitera tel qu'il a été écrit ainsi :
public class Foo | |
{ | |
private int f; | |
public Foo() | |
{ | |
f = 42; | |
System.Console.WriteLine("Foo()"); | |
} | |
public Foo(int i) | |
{ | |
f = 42; | |
System.Console.WriteLine("Foo(int)"); | |
} | |
} |
ce que vous pouvez confirmer dans l'IL généré ci-dessous, mais gardez ce qui suit à l'esprit si vous n'êtes pas familier avec le code IL :
- Il ne faut pas se laisser intimider par son apparente complexité.
- J'ai inclus des commentaires pour souligner les éléments clés.
- J'ai omis certains détails moins cruciaux.
- Il n’est pas nécessaire de saisir tous les détails pour comprendre le concept principal.
.class public auto ansi beforefieldinit Foo extends [System.Runtime]System.Object | |
{ | |
.field private int32 f // `f` is a *private* field of type *int32* (i.e, System.Int32) | |
// Constructor taking no parameters | |
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed | |
{ | |
// IL_0000~IL_0003 is the generated IL for the C# code: `f = 42;` | |
IL_0000: ldarg.0 | |
IL_0001: ldc.i4.s 42 | |
IL_0003: stfld int32 Foo::f | |
// IL_0008~IL_0009: runs base constructor | |
IL_0008: ldarg.0 | |
IL_0009: call instance void [System.Runtime]System.Object::.ctor() | |
// IL_000e~IL_0013: calls `Console.WriteLine("Foo()")` | |
IL_000e: ldstr "Foo()" | |
IL_0013: call void [System.Console]System.Console::WriteLine(string) | |
IL_0018: ret | |
} // end of method Foo::.ctor | |
// Constructor taking 1 int parameter | |
.method public hidebysig specialname rtspecialname instance void .ctor (int32 i) cil managed | |
{ | |
// IL_0000~IL_0003 is the generated IL for the C# code: `f = 42;` | |
// Note that the same code used to assign 42 to `f` was emitted in this constructor as well. | |
IL_0000: ldarg.0 | |
IL_0001: ldc.i4.s 42 | |
IL_0003: stfld int32 Foo::f | |
// rest of constructor removed for brevity. | |
//... | |
IL_0018: ret | |
} // end of method Foo::.ctor | |
} // end of class Foo |
Les lignes 6~22 et 25~36 définissent le constructeur ne prenant aucun paramètre et celui prenant un int (Int32); notez que le code lié à l'initialisation du champ (`f = 42`) a été introduit dans les deux (lignes 9~11 et 29~31).
Dans le prochain article, nous commencerons à explorer le comportement du constructeur de structure, c'est-à-dire la deuxième partie du puzzle qui explique pourquoi le programme en haut de l'article imprime 0.
Comme toujours, tous les commentaires sont bienvenus.
Amuse toi!
No comments:
Post a Comment