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.