Aug 31, 2023

Les structures en C# sont amusantes - Partie 3/9: Initialisation des champs dans les structures

Leia este post em português

Read this post in English.

  1. Les structures en C# sont amusantes
  2. Brève introduction aux Value Types vs Reference Types.
  3. Initialisation des champs dans les structures (cet post).
  4. Comportement des constructeurs dans structures.
  5. Des autres scénarios dans lesquels le comportement des constructeurs de structure peut vous surprendre.
  6. Struct avec des valeurs d'argument par défaut dans les constructeurs, ou, n'êtes-vous pas encore confus ?
  7. Le modificateur `required`de C # 11 ne sauvegardera pas votre c*l emploi.
  8. Structure utilisée comme valeurs d'argument défaut.
  9. 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 :

  1. Il ne faut pas se laisser intimider par son apparente complexité.
  2. J'ai inclus des commentaires pour souligner les éléments clés.
  3. J'ai omis certains détails moins cruciaux.
  4. 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: