Aller au contenu

Les structures

Introduction

Les structures (struct) existent depuis la première version du langage C# et la version 10 a apporté d'importantes modifications. Contrairement aux classes et aux records, les structures sont des types valeur. Elles sont particulièrement adaptées pour représenter des données simples, de petite taille et sans identité propre. Utilisées à bon escient, elles permettent de réduire les allocations mémoire et d'améliorer les performances de certaines applications.

Toutefois attention : une idée reçue consiste à penser qu'une structure est toujours allouée sur la pile (stack) et une classe sur le tas (heap). Ce n'est pas toujours vrai : l'emplacement mémoire dépend du contexte d'utilisation. Par exemple, une structure contenue dans une classe ou dans un tableau est elle aussi stockée dans le tas.

Quand utiliser une structure ?

Les structures sont particulièrement adaptées pour représenter des structures de données concernant des coordonnées (Point), une taille (Size), une couleur (Color), un montant (Money), une durée, une température, etc. D'une manière générale, l'utilisation d'une structure est adaptée dans les cas suivants :

  • Elle représente une valeur et non un objet métier

  • Elle est de "petite taille"

  • Elle est fréquemment créée

  • Elle n'a pas besoin d'être héritée

Aussi, le .NET Framework utilise très largement les structures, au travers des types suivants : DateTime, DateOnly, TimeOnly, Guid, TimeSpan, decimal, etc.

Présentation

Une structure se déclare à l'aide du mot-clé struct :

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

Elle peut être utilisée comme n'importe quel autre type :

var point = new Point
{
    X = 10,
    Y = 20
};

Depuis la version 10 du langage C#, les structures peuvent également utiliser des constructeurs explicites :

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Ainsi on peut écrire :

var point = new Point(10, 20);

Comportement d'une structure lors de l'affectation

La principale différence avec une classe est qu'une structure est un type valeur. Lorsque nous affectons une structure à une autre variable :

var point1 = new Point(10, 20);

var point2 = point1;

Les valeurs contenues dans point1 sont copiées dans point2. Si nous modifions ensuite point2 :

point2.X = 100;

Alors :

point1.X == 10
point2.X == 100

Les deux propriétés sont totalement indépendantes.

À l'inverse, avec une classe, les deux variables point1 et point2 auraient référencé le même objet, ainsi la modification d'une propriété d'une variable aurait affecté l'autre (car il s'agit du même objet).

Comparaison entre deux structures

Les structures ne possèdent pas de notion d'identité. Lorsque nous écrivons :

var point1 = new Point(10, 20);
var point2 = new Point(10, 20);

var result = point1.Equals(point2);

La variable result vaut true, car la méthode Equals() compare les valeurs contenues dans la structure.

Les limites des structures

Les structures possèdent également quelques limitations. Contrairement aux classes :

  • Elles ne peuvent pas hériter d'une autre structure ou d'une classe

  • Elles ne peuvent pas servir de type de base pour une autre structure

  • Elles sont copiées à chaque affectation ou passage de paramètres (sauf si utilisation de ref, in, out)

Pour cette raison, nous devons si possible ne pas créer des structures volumineuses, car leur copie peut devenir coûteuse.

Les structures immuables

Comme pour les records, il est souvent recommandé de rendre les structures immuables. Pour cela, le langage C# propose les mots-clés readonly struct :

public readonly struct Point
{

    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Une fois créée, une telle structure ne peut plus être modifiée.