Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les enregistrements représentent des agrégats simples de valeurs nommées, éventuellement avec des membres. Ils peuvent être des structs ou des types référence. Ils sont des types de référence par défaut.
Syntaxe
[ attributes ]
type [accessibility-modifier] typename =
[accessibility-modifier] {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
[ member-list ]
Avant accessibility modifier d’affecter typename la visibilité de l’ensemble du type et est public par défaut. La deuxième accessibility modifier affecte uniquement le constructeur et les champs.
Remarques
Dans la syntaxe précédente, typename est le nom du type d’enregistrement, label1 et label2 sont des noms de valeurs, appelés étiquettes, et type1 et type2 sont les types de ces valeurs.
member-list est la liste facultative des membres pour le type. Vous pouvez utiliser l’attribut [<Struct>] pour créer un enregistrement de struct plutôt qu’un enregistrement qui est un type de référence.
Voici quelques exemples.
// Labels are separated by semicolons when defined on the same line.
type Point = { X: float; Y: float; Z: float }
// You can define labels on their own line with or without a semicolon.
type Customer =
{ First: string
Last: string
SSN: uint32
AccountNumber: uint32 }
// A struct record.
[<Struct>]
type StructPoint = { X: float; Y: float; Z: float }
Lorsque chaque étiquette se trouve sur une ligne distincte, le point-virgule est facultatif.
Vous pouvez définir des valeurs dans les expressions appelées expressions d’enregistrement. Le compilateur déduit le type des étiquettes utilisées (si les étiquettes sont suffisamment distinctes de celles d’autres types d’enregistrements). Accolades ({ }) entourent l’expression d’enregistrement. Le code suivant montre une expression d’enregistrement qui initialise un enregistrement avec trois éléments float avec des étiquettes xet yz.
let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }
N’utilisez pas le formulaire raccourci s’il peut y avoir un autre type qui possède également les mêmes étiquettes.
type Point = { X: float; Y: float; Z: float }
type Point3D = { X: float; Y: float; Z: float }
// Ambiguity: Point or Point3D?
let mypoint3D = { X = 1.0; Y = 1.0; Z = 0.0 }
Les étiquettes du type le plus récemment déclaré sont prioritaires sur celles du type précédemment déclaré. Par conséquent, dans l’exemple précédent, mypoint3D elle est déduite d’être Point3D. Vous pouvez spécifier explicitement le type d’enregistrement, comme dans le code suivant.
let myPoint1 = { Point.X = 1.0; Y = 1.0; Z = 0.0 }
Les méthodes peuvent être définies pour les types d’enregistrements comme pour les types de classes.
Création d’enregistrements à l’aide d’expressions d’enregistrement
Vous pouvez initialiser des enregistrements à l’aide des étiquettes définies dans l’enregistrement. Expression qui effectue cette opération est appelée expression d’enregistrement. Utilisez des accolades pour placer l’expression d’enregistrement et utiliser le point-virgule comme délimiteur.
L’exemple suivant montre comment créer un enregistrement.
type MyRecord = { X: int; Y: int; Z: int }
let myRecord1 = { X = 1; Y = 2; Z = 3 }
Les points-virgules après le dernier champ de l’expression d’enregistrement et dans la définition de type sont facultatifs, que les champs soient tous en une seule ligne.
Lorsque vous créez un enregistrement, vous devez fournir des valeurs pour chaque champ. Vous ne pouvez pas faire référence aux valeurs d’autres champs dans l’expression d’initialisation pour n’importe quel champ.
Dans le code suivant, le type de myRecord2 données est déduit des noms des champs. Si vous le souhaitez, vous pouvez spécifier explicitement le nom de type.
let myRecord2 =
{ MyRecord.X = 1
MyRecord.Y = 2
MyRecord.Z = 3 }
Une autre forme de construction d’enregistrement peut être utile lorsque vous devez copier un enregistrement existant et éventuellement modifier certaines des valeurs de champ. La ligne de code suivante illustre cela.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Cette forme de l’expression d’enregistrement est appelée expression de copie et de mise à jour de l’enregistrement.
Les enregistrements sont immuables par défaut ; Toutefois, vous pouvez facilement créer des enregistrements modifiés à l’aide d’une copie et d’une expression de mise à jour. Vous pouvez également spécifier explicitement un champ mutable.
type Car =
{ Make: string
Model: string
mutable Odometer: int }
let myCar =
{ Make = "Fabrikam"
Model = "Coupe"
Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
N’utilisez pas l’attribut DefaultValue avec les champs d’enregistrement. Une meilleure approche consiste à définir les instances par défaut d’enregistrements avec des champs qui sont initialisés en valeurs par défaut, puis à utiliser une expression de copie et de mise à jour d’enregistrement pour définir les champs qui diffèrent des valeurs par défaut.
// Rather than use [<DefaultValue>], define a default record.
type MyRecord =
{ Field1 : int
Field2 : int }
let defaultRecord1 = { Field1 = 0; Field2 = 0 }
let defaultRecord2 = { Field1 = 1; Field2 = 25 }
// Use the with keyword to populate only a few chosen fields
// and leave the rest with default values.
let rr3 = { defaultRecord1 with Field2 = 42 }
Création d’enregistrements récursifs mutuellement
Parfois, lors de la création d’un enregistrement, vous souhaiterez peut-être qu’il dépend d’un autre type que vous souhaitez définir par la suite. Il s’agit d’une erreur de compilation, sauf si vous définissez les types d’enregistrements pour qu’ils soient récursifs mutuellement.
La définition d’enregistrements récursifs mutuellement est effectuée avec le and mot clé. Cela vous permet de lier 2 types d’enregistrements ou plus ensemble.
Par exemple, le code suivant définit un type et Person un Address type comme récursifs mutuellement :
// Create a Person type and use the Address type that is not defined
type Person =
{ Name: string
Age: int
Address: Address }
// Define the Address type which is used in the Person record
and Address =
{ Line1: string
Line2: string
PostCode: string
Occupant: Person }
Pour créer des instances des deux, procédez comme suit :
// Create a Person type and use the Address type that is not defined
let rec person =
{
Name = "Person name"
Age = 12
Address =
{
Line1 = "line 1"
Line2 = "line 2"
PostCode = "abc123"
Occupant = person
}
}
Si vous deviez définir l’exemple précédent sans le and mot clé, il ne serait pas compilé. Le and mot clé est requis pour les définitions récursives mutuellement.
Correspondance des modèles avec les enregistrements
Les enregistrements peuvent être utilisés avec des critères correspondants. Vous pouvez spécifier certains champs explicitement et fournir des variables pour d’autres champs qui seront affectés lorsqu’une correspondance se produit. L’exemple de code suivant illustre cela.
type Point3D = { X: float; Y: float; Z: float }
let evaluatePoint (point: Point3D) =
match point with
| { X = 0.0; Y = 0.0; Z = 0.0 } -> printfn "Point is at the origin."
| { X = xVal; Y = 0.0; Z = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
| { X = 0.0; Y = yVal; Z = 0.0 } -> printfn "Point is on the y-axis. Value is %f." yVal
| { X = 0.0; Y = 0.0; Z = zVal } -> printfn "Point is on the z-axis. Value is %f." zVal
| { X = xVal; Y = yVal; Z = zVal } -> printfn "Point is at (%f, %f, %f)." xVal yVal zVal
evaluatePoint { X = 0.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 100.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 10.0; Y = 0.0; Z = -1.0 }
La sortie de ce code est la suivante.
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Enregistrements et membres
Vous pouvez spécifier des membres sur des enregistrements comme vous pouvez avec des classes. Il n’existe aucune prise en charge des champs. Une approche courante consiste à définir un Default membre statique pour faciliter la construction des enregistrements :
type Person =
{ Name: string
Age: int
Address: string }
static member Default =
{ Name = "Phillip"
Age = 12
Address = "123 happy fun street" }
let defaultPerson = Person.Default
Si vous utilisez un auto-identificateur, cet identificateur fait référence à l’instance de l’enregistrement dont le membre est appelé :
type Person =
{ Name: string
Age: int
Address: string }
member this.WeirdToString() =
this.Name + this.Address + string this.Age
let p = { Name = "a"; Age = 12; Address = "abc123" }
let weirdString = p.WeirdToString()
Modificateurs d’accessibilité sur les enregistrements
Le code suivant illustre l’utilisation de modificateurs d’accessibilité. Il existe trois fichiers dans le projet : Module1.fs, Test1.fset Test2.fs. Un type d’enregistrement interne et un type d’enregistrement avec un constructeur privé sont définis dans Module1.
// Module1.fs
module Module1
type internal internalRecd = { X: int }
type recdWithInternalCtor = private { Y: int }
Dans le Test1.fs fichier, l’enregistrement interne doit être initialisé avec le internal modificateur d’accès, c’est parce que le niveau de protection de la valeur et l’enregistrement doivent correspondre, et les deux doivent appartenir au même assembly.
// Test1.fs
module Test1
open Module1
let myInternalRecd1 = { X = 2 } // This line will cause a compiler error.
let internal myInternalRecd2 = { X = 4 } // This is OK
Dans le Test2.fs fichier, l’enregistrement avec le constructeur privé ne peut pas être initialisé directement en raison du niveau de protection du constructeur.
// Test2.fs
module Test2
open Module1
let myRecdWithInternalCtor = { Y = 6 } // This line will cause a compiler error.
Pour plus d’informations sur les modificateurs d’accessibilité, consultez l’article Contrôle d’accès .
Différences entre les enregistrements et les classes
Les champs d’enregistrement diffèrent des champs de classe, car ils sont automatiquement exposés en tant que propriétés, et ils sont utilisés dans la création et la copie d’enregistrements. La construction d’enregistrements diffère également de la construction de classe. Dans un type d’enregistrement, vous ne pouvez pas définir de constructeur. Au lieu de cela, la syntaxe de construction décrite dans cette rubrique s’applique. Les classes n’ont aucune relation directe entre les paramètres de constructeur, les champs et les propriétés.
Comme les types d’union et de structure, les enregistrements ont une sémantique d’égalité structurelle. Les classes ont une sémantique d’égalité de référence. L’exemple de code suivant illustre ceci.
type RecordTest = { X: int; Y: int }
let record1 = { X = 1; Y = 2 }
let record2 = { X = 1; Y = 2 }
if (record1 = record2) then
printfn "The records are equal."
else
printfn "The records are unequal."
La sortie de ce code est la suivante :
The records are equal.
Si vous écrivez le même code avec des classes, les deux objets de classe seraient inégaux, car les deux valeurs représenteraient deux objets sur le tas et seules les adresses seraient comparées (sauf si le type de classe remplace la System.Object.Equals méthode).
Si vous avez besoin d’une égalité de référence pour les enregistrements, ajoutez l’attribut [<ReferenceEquality>] au-dessus de l’enregistrement.