Propriétés (F#)
Les propriétés sont des membres qui représentent des valeurs associées à un objet.
// Property that has both get and set defined.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with [accessibility-modifier] get() =
get-function-body
and [accessibility-modifier] set parameter =
set-function-body
// Alternative syntax for a property that has get and set.
[ attributes-for-get ]
[ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName =
get-function-body
[ attributes-for-set ]
[ static ] member [accessibility-modifier-for-set] [self-identifier.]PropertyName
with set parameter =
set-function-body
// Property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName =
get-function-body
// Alternative syntax for property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with get() =
get-function-body
// Property that has set only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName
with set parameter =
set-function-body
// Automatically implemented properties.
[attributes ]
[ static ] member val [accessibility-modifier ] PropertyName = initialization-expression [ with get, set ]
Notes
Les propriétés constituent la relation « has a » (a un) dans la programmation orientée objet. Elles représentent les données associées à des instances d'objet ou, pour des propriétés statiques, au type.
Vous pouvez déclarer des propriétés de deux façons, selon que vous souhaitez spécifier explicitement la valeur sous-jacente (également appelée magasin de stockage) pour la propriété, ou si vous souhaitez permettre au compilateur de générer automatiquement le magasin de stockage pour vous. En général, vous devez utiliser la méthode la plus explicite si la propriété a une implémentation non triviale et la façon automatique lorsque la propriété est simplement un wrapper simple pour une valeur ou variable. Pour déclarer un événement explicitement, utilisez le mot clé member. Cette syntaxe déclarative est suivie par la syntaxe qui spécifie les méthodes get et set, aussi appelées accesseurs. Les différentes formes de la syntaxe explicites présentée dans la section syntaxe sont utilisées pour les propriétés en lecture/écriture, en lecture seule et en écriture seule. Pour les propriétés en lecture seule, définissez uniquement une méthode get ; pour les propriétés en écriture seule, définissez uniquement une méthode set. Notez que lorsqu'une propriété a des accesseurs get et set, l'autre syntaxe vous permet de spécifier des attributs et des modificateurs d'accessibilité qui sont différents pour chaque accesseur, comme indiqué dans le code suivant.
// A read-only property.
member this.MyReadOnlyProperty = myInternalValue
// A write-only property.
member this.MyWriteOnlyProperty with set (value) = myInternalValue <- value
// A read-write property.
member this.MyReadWriteProperty
with get () = myInternalValue
and set (value) = myInternalValue <- value
Pour les propriétés en lecture/écriture, qui ont à la fois une méthode get et set, l'ordre de get et de set peut être inversé. Vous pouvez aussi, au lieu d'utiliser la syntaxe combinée, fournir uniquement la syntaxe indiquée pour get et uniquement la syntaxe indiquée pour set. De cette façon, vous pouvez commenter plus facilement la méthode individuelle get ou set, si cela peut vous être utile. Cette solution de remplacement de la syntaxe combinée est illustrée dans le code suivant.
member this.MyReadWriteProperty with get () = myInternalValue
member this.MyReadWriteProperty with set (value) = myInternalValue <- value
Les valeurs privées qui contiennent les données pour les propriétés sont appelées magasins de stockage. Pour faire en sorte que le compilateur crée le magasin de stockage automatiquement, utilisez les mots clés member val, omettez l'auto-identificateur, puis fournissez une expression pour initialiser la propriété. Si la propriété doit être mutable, incluez with get, set. Par exemple, le type de classe suivant inclut deux propriétés automatiquement implémentées. Property1 est en lecture seule et est initialisé à l'argument fourni au constructeur principal, et Property2 est une propriété définissable initialisée à une chaîne vide :
type MyClass(property1 : int) =
member val Property1 = property1
member val Property2 = "" with get, set
Les propriétés automatiquement implémentées font partie de l'initialisation d'un type, elles doivent être inclus avant toutes les autres définitions de membre, comme les liaisons let et les liaisons do dans une définition de type. Notez que l'expression qui initialise une propriété implémentée automatiquement est évaluée uniquement à l'initialisation, et pas à chaque fois que la propriété est accessible. Ce comportement contraste avec le comportement d'une propriété explicitement implémentée. La signification effective est que le code pour initialiser ces propriétés est ajouté au constructeur d'une classe. Prenons le code suivant qui illustre cette différence :
type MyClass() =
let random = new System.Random()
member val AutoProperty = random.Next() with get, set
member this.ExplicitProperty = random.Next()
let class1 = new MyClass()
printfn "class1.AutoProperty = %d" class1.AutoProperty
printfn "class1.AutoProperty = %d" class1.AutoProperty
printfn "class1.ExplicitProperty = %d" class1.ExplicitProperty
printfn "class1.ExplicitProperty = %d" class1.ExplicitProperty
Sortie
La sortie du code précédent indique que la valeur d'AutoProperty est inchangée lorsqu´elle est appelée plusieurs fois, alors que l'ExplicitProperty change chaque fois qu´elle est appelée. Cela indique que l'expression pour une propriété implémentée automatiquement n'est pas évaluée chaque fois, de même que la méthode d'accesseur Get pour la propriété explicite.
Avertissement
Il existe certaines bibliothèques, telles qu'Entity Framework (System.Data.Entity) qui exécutent des opérations personnalisées dans les constructeurs de classe de base qui ne fonctionnent pas correctement avec l'initialisation des propriétés automatiquement implémentées.Dans ces cas, essayez d´utiliser les propriétés explicites.
Les propriétés peuvent être des membres de classes, de structures, d'unions discriminées, d'enregistrements, d'interfaces et d'extensions de type. Elles peuvent également être définies dans des expressions d'objet.
Vous pouvez appliquer des attributs à des propriétés. Pour cela, écrivez l'attribut sur une ligne séparée avant la propriété. Pour plus d’informations, consultez Attributs (F#).
Par défaut, les propriétés sont publiques. Vous pouvez également appliquer des modificateurs d'accessibilité à des propriétés. Pour cela, ajoutez le modificateur d'accessibilité immédiatement avant le nom de la propriété s'il est censé s'appliquer aux méthodes get et set ; ajoutez-le avant les mots clés get et set si chaque accesseur requiert une accessibilité différente. accessibility-modifier peut avoir l'une des valeurs suivantes : public, private ou internal. Pour plus d’informations, consultez Contrôle d'accès (F#).
Les implémentations de propriété sont exécutées chaque fois qu'une propriété fait l'objet d'un accès.
Propriétés statiques et d'instance
Les propriétés peuvent être des propriétés statiques ou des propriétés d'instance. Les propriétés statiques peuvent être appelées sans instance et sont utilisées pour des valeurs associées au type, et non à des objets individuels. Pour les propriétés statiques, omettez l'auto-identificateur. L'auto-identificateur est requis pour les propriétés d'instance.
La définition de propriété statique suivante est basée sur un scénario dans lequel un champ statique myStaticValue est le magasin de stockage pour la propriété.
static member MyStaticProperty
with get() = myStaticValue
and set(value) = myStaticValue <- value
Les propriétés peuvent également être de type tableau, auquel cas elles sont appelées propriétés indexées. Pour plus d’informations, consultez Propriétés indexées (F#).
Annotation de type pour les propriétés
Dans de nombreux cas, le compilateur dispose de suffisamment d'informations pour déduire le type d'une propriété à partir du type du magasin de stockage. Toutefois, vous pouvez définir le type explicitement en ajoutant une annotation de type.
// To apply a type annotation to a property that does not have an explicit
// get or set, apply the type annotation directly to the property.
member this.MyProperty1 : int = myInternalValue
// If there is a get or set, apply the type annotation to the get or set method.
member this.MyProperty2 with get() : int = myInternalValue
Utilisation d'accesseurs de jeu de propriétés
Vous pouvez définir des propriétés qui fournissent des accesseurs set à l'aide de l'opérateur <-.
// Assume that the constructor argument sets the initial value of the
// internal backing store.
let mutable myObject = new MyType(10)
myObject.MyProperty <- 20
printfn "%d" (myObject.MyProperty)
Le résultat est 20.
Propriétés abstraites
Les propriétés peuvent être abstraites. Comme pour les méthodes, le terme abstrait signifie simplement qu'un dispatch virtuel est associé à la propriété. Les propriétés abstraites peuvent être vraiment abstraites, c'est-à-dire sans définition dans la même classe. La classe qui contient une telle propriété est par conséquent une classe abstraite. Le terme abstrait peut aussi simplement signifier qu'une propriété est virtuelle ; dans ce cas, une définition doit être présente dans la même classe. Notez que les propriétés abstraites ne doivent pas être privées, et que si un accesseur est abstrait, l'autre doit l'être également. Pour plus d'informations sur les classes abstraites, consultez Classes abstraites (F#).
// Abstract property in abstract class.
// The property is an int type that has a get and
// set method
[<AbstractClass>]
type AbstractBase() =
abstract Property1 : int with get, set
// Implementation of the abstract property
type Derived1() =
inherit AbstractBase()
let mutable value = 10
override this.Property1 with get() = value and set(v : int) = value <- v
// A type with a "virtual" property.
type Base1() =
let mutable value = 10
abstract Property1 : int with get, set
default this.Property1 with get() = value and set(v : int) = value <- v
// A derived type that overrides the virtual property
type Derived2() =
inherit Base1()
let mutable value2 = 11
override this.Property1 with get() = value2 and set(v) = value2 <- v