Structures

Une structure est un type d’objet compact qui peut être plus efficace qu’une classe pour les types ayant une petite quantité de données et un comportement simple.

Syntaxe

[ attributes ]
type [accessibility-modifier] type-name =
    struct
        type-definition-elements-and-members
    end
// or
[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
    type-definition-elements-and-members

Notes

Les structures sont des types valeur, ce qui signifie qu’elles sont stockées directement sur la pile ou inline dans le parent du type quand elles sont utilisées comme des champs ou des éléments de tableau. Contrairement aux classes et aux enregistrements, les structures ont une sémantique de passage par valeur. Cela signifie qu'elles sont principalement utilisées pour les petits volumes de données qui sont fréquemment sollicités et copiés.

Dans la syntaxe précédente, deux formes sont présentes. La première n'est pas la syntaxe simplifiée, mais elle est néanmoins fréquemment utilisée, car quand vous employez les mots clés struct et end, vous pouvez omettre l'attribut StructAttribute qui apparaît dans la seconde forme. Vous pouvez abréger StructAttribute en Struct.

type-definition-elements-and-members dans la syntaxe précédente représente les définitions et les déclarations de membre. Les structures peuvent avoir des constructeurs et des champs modifiables et non modifiables, et peuvent déclarer des membres et des implémentations d'interface. Pour plus d’informations, consultez Membres.

Les structures ne peuvent pas participer à l’héritage, ne peuvent pas contenir les liaisons let ou do, et ne peuvent pas comporter de manière récursive les champs de leur propre type (bien qu’elles puissent contenir des cellules de référence qui référencent leur propre type).

Étant donné que les structures n’autorisent pas les liaisons let, vous devez déclarer leurs champs à l’aide du mot clé val. Le mot clé val définit un champ et son type, mais n'autorise pas l'initialisation. À la place, les déclarations val sont initialisées à la valeur zéro ou null. Pour cette raison, les structures ayant un constructeur implicite (c’est-à-dire les paramètres qui sont fournis immédiatement après le nom de la structure dans la déclaration) requièrent que les déclarations val soient annotées avec l’attribut DefaultValue. Les structures ayant un constructeur défini prennent toujours en charge l'initialisation à zéro. Par conséquent, l'attribut DefaultValue est une déclaration selon laquelle une telle valeur égale à zéro est valide pour le champ. Les constructeurs implicites pour les structures n'exécutent aucune action, car les liaisons let et do ne sont pas autorisées sur le type, mais les valeurs de paramètre de constructeur implicite passées sont disponibles en tant que champs privés.

Les constructeurs explicites peuvent impliquer l'initialisation des valeurs de champ. Quand une structure a un constructeur explicite, elle prend toujours en charge l'initialisation à zéro ; toutefois, vous n'utilisez pas l'attribut DefaultValue sur les déclarations val, car il est en conflit avec le constructeur explicite. Pour plus d’informations sur les déclarations val, consultez Champs explicites : mot clé val.

Les attributs et les modificateurs d'accessibilité sont autorisés sur les structures et suivent les mêmes règles que celles des autres types. Pour plus d’informations, consultez Attributs et Contrôle d’accès.

Les exemples de code suivants illustrent des définitions de structure.

// In Point3D, three immutable values are defined.
// x, y, and z will be initialized to 0.0.
type Point3D =
    struct
        val x: float
        val y: float
        val z: float
    end

// In Point2D, two immutable values are defined.
// It also has a member which computes a distance between itself and another Point2D.
// Point2D has an explicit constructor.
// You can create zero-initialized instances of Point2D, or you can
// pass in arguments to initialize the values.
type Point2D =
    struct
        val X: float
        val Y: float
        new(x: float, y: float) = { X = x; Y = y }

        member this.GetDistanceFrom(p: Point2D) =
            let dX = (p.X - this.X) ** 2.0
            let dY = (p.Y - this.Y) ** 2.0

            dX + dY |> sqrt
    end

Structs ByRefLike

Vous pouvez définir vos propres structs qui peuvent adhérer à une sémantique comme byref : pour plus d’informations, consultez Byrefs. Pour cela, vous utilisez l’attribut IsByRefLikeAttribute :

open System
open System.Runtime.CompilerServices

[<IsByRefLike; Struct>]
type S(count1: Span<int>, count2: Span<int>) =
    member x.Count1 = count1
    member x.Count2 = count2

IsByRefLike n’implique pas Struct. Les deux doivent être présents dans le type.

Un struct "se comportant comme un type byref" en F# est un type valeur lié à la pile. Il n’est jamais alloué sur le tas managé. Un struct se comportant comme un type byref est utile pour la programmation haute performance, car il est appliqué avec un ensemble de vérifications fortes sur la durée de vie et la non-capture. Les règles sont les suivantes :

  • Ils peuvent être utilisés en tant que paramètres de fonction, paramètres de méthode, variables locales, retours de méthode.
  • Ils ne peuvent pas être des membres statiques ou des membres d’instance d’une classe ou d’un struct normal.
  • Ils ne peuvent pas être capturés par une construction de fermeture (méthodes async ou expressions lambda).
  • Ils ne peuvent pas être utilisés en tant que paramètres génériques.

Bien que ces règles restreignent très fortement l’utilisation, elles sont nécessaires pour garantir l’exécution sécurisée de calculs haute performance.

Structs ReadOnly

Vous pouvez annoter des structs avec l’attribut IsReadOnlyAttribute. Par exemple :

[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
    member x.Count1 = count1
    member x.Count2 = count2

IsReadOnly n’implique pas Struct. Vous devez ajouter les deux pour avoir un struct IsReadOnly.

L’utilisation de cet attribut émet des métadonnées permettant à F# et C# de le traiter comme inref<'T> et in ref, respectivement.

La définition d’une valeur mutable à l’intérieur d’un struct en lecture seule génère une erreur.

Unions discriminées et enregistrements de struct

Vous pouvez représenter des enregistrements et des unions discriminées sous forme de structs avec l’attribut [<Struct>]. Consultez chaque article pour en savoir plus.

Voir aussi