Constructeurs (F#)
Cette rubrique décrit comment définir et utiliser des constructeurs pour créer et initialiser des objets de classe et de structure.
Construction d'objets de classe
Les objets de type de classe possèdent des constructeurs. Il existe deux types de constructeurs. L'un est le constructeur principal, dont les paramètres apparaissent entre parenthèses juste après le nom du type. Les autres, qui sont facultatifs, sont spécifiés à l'aide du mot clé new. Ces constructeurs supplémentaires doivent appeler le constructeur principal.
Le constructeur principal contient les liaisons let et do qui apparaissent au début de la définition de classe. Une liaison let déclare les champs privés et méthodes de la classe ; une liaison do exécute le code. Pour plus d'informations sur les liaisons let dans les constructeurs de classe, consultez Liaisons let dans les classes (F#). Pour plus d'informations sur les liaisons do dans les constructeurs, consultez Liaisons do dans les classes (F#).
Que le constructeur que vous voulez appeler soit un constructeur principal ou un constructeur supplémentaire, vous pouvez créer des objets à l'aide d'une expression new, avec ou sans le mot clé new facultatif. Vous initialisez vos objets avec les arguments de constructeur, soit en répertoriant dans l'ordre les arguments, ceux-ci devant être séparés par des virgules et placés entre parenthèses, soit en utilisant des arguments nommés et des valeurs entre parenthèses. Vous pouvez aussi définir des propriétés sur un objet au cours de la construction de l'objet en utilisant les noms de propriété et en assignant des valeurs, au même titre que des arguments de constructeur nommés.
Le code suivant illustre une classe comprenant un constructeur et différentes façons de créer des objets.
// This class has a primary constructor that takes three arguments
// and an additional constructor that calls the primary constructor.
type MyClass(x0, y0, z0) =
let mutable x = x0
let mutable y = y0
let mutable z = z0
do
printfn "Initialized object that has coordinates (%d, %d, %d)" x y z
member this.X with get() = x and set(value) = x <- value
member this.Y with get() = y and set(value) = y <- value
member this.Z with get() = z and set(value) = z <- value
new() = MyClass(0, 0, 0)
// Create by using the new keyword.
let myObject1 = new MyClass(1, 2, 3)
// Create without using the new keyword.
let myObject2 = MyClass(4, 5, 6)
// Create by using named arguments.
let myObject3 = MyClass(x0 = 7, y0 = 8, z0 = 9)
// Create by using the additional constructor.
let myObject4 = MyClass()
La sortie est la suivante.
Initialized object that has coordinates (1, 2, 3)
Initialized object that has coordinates (4, 5, 6)
Initialized object that has coordinates (7, 8, 9)
Initialized object that has coordinates (0, 0, 0)
Construction de structures
Les structures respectent toutes les règles des classes. Par conséquent, vous pouvez avoir un constructeur principal et fournir des constructeurs supplémentaires à l'aide de new. Toutefois, il existe une différence importante entre les structures et les classes : les structures peuvent avoir un constructeur par défaut (c'est-à-dire, sans arguments), même si aucun constructeur principal n'est défini. Le constructeur par défaut initialise tous les champs selon la valeur par défaut pour ce type, généralement zéro ou son équivalent. Tous les constructeurs que vous définissez pour des structures doivent avoir au moins un argument de manière à ce qu'ils ne soient pas en conflit avec le constructeur par défaut.
Par ailleurs, les structures ont souvent des champs qui sont créés à l'aide du mot clé val ; des classes peuvent également avoir ces champs. Les structures et les classes dont des champs ont été définis à l'aide du mot clé val peuvent également être initialisées dans des constructeurs supplémentaires à l'aide d'expressions d'enregistrement, comme indiqué dans le code suivant.
type MyStruct =
struct
val X : int
val Y : int
val Z : int
new(x, y, z) = { X = x; Y = y; Z = z }
end
let myStructure1 = new MyStruct(1, 2, 3)
Pour plus d’informations, consultez Champs explicites : mot clé val (F#).
Exécution d'effets secondaires dans les constructeurs
Un constructeur principal dans une classe peut exécuter du code dans une liaison do. Toutefois, comment faire si vous devez exécuter du code dans un constructeur supplémentaire sans liaison do ? Dans ce cas, utilisez le mot clé then.
// Executing side effects in the primary constructor and
// additional constructors.
type Person(nameIn : string, idIn : int) =
let mutable name = nameIn
let mutable id = idIn
do printfn "Created a person object."
member this.Name with get() = name and set(v) = name <- v
member this.ID with get() = id and set(v) = id <- v
new() =
Person("Invalid Name", -1)
then
printfn "Created an invalid person object."
let person1 = new Person("Humberto Acevedo", 123458734)
let person2 = new Person()
Les effets secondaires du constructeur principal s'exécutent encore. Par conséquent, la sortie est la suivante.
Created a person object.
Created a person object.
Created an invalid person object.
Auto-identificateurs dans les constructeurs
Dans d'autres membres, vous fournissez un nom pour l'objet actif dans la définition de chaque membre. Vous pouvez également placer l'auto-identificateur sur la première ligne de la définition de classe à l'aide du mot clé as, immédiatement après les paramètres du constructeur. L'exemple suivant illustre cette syntaxe.
type MyClass1(x) as this =
// This use of the self identifier produces a warning - avoid.
let x1 = this.X
// This use of the self identifier is acceptable.
do printfn "Initializing object with X =%d" this.X
member this.X = x
Dans les constructeurs supplémentaires, vous pouvez aussi définir un auto-identificateur en plaçant la clause as juste après les paramètres du constructeur. L'exemple suivant illustre cette syntaxe.
type MyClass2(x : int) =
member this.X = x
new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X
Des problèmes peuvent se produire lorsque vous essayez d'utiliser un objet qui n'est pas encore entièrement défini. Par conséquent, les utilisations de l'auto-identificateur peuvent amener le compilateur à émettre un avertissement et à insérer des contrôles supplémentaires pour vérifier que les membres d'un objet ne sont pas atteints avant l'initialisation de l'objet. Vous devez utiliser uniquement l'auto-identificateur dans les liaisons do du constructeur principal, ou après le mot clé then dans les constructeurs supplémentaires.
Le nom de l'auto-identificateur ne doit pas nécessairement être this. Vous pouvez utiliser un identificateur valide quelconque.
Assignation de valeurs aux propriétés lors de l'initialisation
Vous pouvez assigner des valeurs aux propriétés d'un objet de classe dans le code d'initialisation en ajoutant une liste d'assignations sous la forme property = value à la liste d'arguments pour un constructeur. Ceci est illustré dans l'exemple de code suivant.
type Account() =
let mutable balance = 0.0
let mutable number = 0
let mutable firstName = ""
let mutable lastName = ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(AccountNumber=8782108,
FirstName="Darren", LastName="Parker",
Balance=1543.33)
La version suivante du code précédent illustre la combinaison d'arguments ordinaires, d'arguments facultatifs et de paramètres de propriété dans un appel de constructeur.
type Account(accountNumber : int, ?first: string, ?last: string, ?bal : float) =
let mutable balance = defaultArg bal 0.0
let mutable number = accountNumber
let mutable firstName = defaultArg first ""
let mutable lastName = defaultArg last ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(8782108, bal = 543.33,
FirstName="Raman", LastName="Iyer")
Constructeurs statiques ou de type
En plus de spécifier le code pour la création d'objets, les liaisons let et do statiques peuvent être créées dans des types de classe qui s'exécutent avant la première utilisation du type afin de procéder à l'initialisation au niveau du type. Pour plus d'informations, consultez Liaisons let dans les classes (F#) et Liaisons do dans les classes (F#).