Héritage (F#)
L'héritage est utilisé pour modéliser la relation « is-a » (est-un), ou sous-typage, dans la programmation orientée objet.
Spécification de relations d'héritage
Pour spécifier des relations d'héritage, utilisez le mot clé inherit dans une déclaration de classe. La forme syntaxique de base est illustrée dans l'exemple suivant.
type MyDerived(...) =
inherit MyBase(...)
Une classe peut avoir au plus une classe de base directe. Si vous ne spécifiez pas de classe de base à l'aide du mot clé inherit, la classe hérite implicitement de Object.
Membres hérités
Si une classe hérite d'une autre classe, les méthodes et membres de la classe de base sont disponibles aux utilisateurs de la classe dérivée comme s'ils étaient membres directs de la classe dérivée.
Les liaisons let et les paramètres de constructeur sont privés vis-à-vis d'une classe ; par conséquent, ils ne sont pas accessibles à partir de classes dérivées.
Le mot clé base est disponible dans les classes dérivées et fait référence à l'instance de classe de base. Il est utilisé comme auto-identificateur.
Méthodes virtuelles et substitutions
En F#, les méthodes (et les propriétés) virtuelles ne fonctionnent pas tout à fait de la même façon que dans d'autres langages .NET. Pour déclarer un nouveau membre virtuel, vous utilisez le mot clé abstract. Ceci s'applique que vous fournissiez ou non une implémentation par défaut pour cette méthode. Ainsi, une définition complète d'une méthode virtuelle dans une classe de base suit ce modèle :
abstract member method-name : type
default self-identifier.method-name argument-list = method-body
Et dans une classe dérivée, une substitution de cette méthode virtuelle suit ce modèle :
override self-identifier.method-name argument-list = method-body
Si vous omettez l'implémentation par défaut dans la classe de base, cette dernière devient une classe abstraite.
L'exemple de code suivant illustre la déclaration d'une nouvelle méthode virtuelle function1 dans une classe de base, et indique comment la substituer dans une classe dérivée.
type MyClassBase1() =
let mutable z = 0
abstract member function1 : int -> int
default u.function1(a : int) = z <- z + a; z
type MyClassDerived1() =
inherit MyClassBase1()
override u.function1(a: int) = a + 1
Constructeurs et héritage
Le constructeur pour la classe de base doit être appelé dans la classe dérivée. Les arguments pour le constructeur de classe de base apparaissent dans la liste d'arguments dans la clause inherit. Les valeurs utilisées doivent être déterminées à partir des arguments fournis au constructeur de classe dérivée.
Le code suivant montre une classe de base et une classe dérivée, où la classe dérivée appelle le constructeur de classe de base dans la clause d'héritage :
type MyClassBase2(x: int) =
let mutable z = x * x
do for i in 1..z do printf "%d " i
type MyClassDerived2(y: int) =
inherit MyClassBase2(y * 2)
do for i in 1..y do printf "%d " i
Dans le cas de plusieurs constructeurs, le code suivant peut être utilisé. La première ligne des constructeurs de classe dérivée est la clause inherit et les champs apparaissent en tant que champs explicites déclarés avec le mot clé val. Pour plus d'informations, consultez Champs explicites : le mot-clé val.
type BaseClass =
val string1 : string
new (str) = { string1 = str }
new () = { string1 = "" }
type DerivedClass =
inherit BaseClass
val string2 : string
new (str1, str2) = { inherit BaseClass(str1); string2 = str2 }
new (str2) = { inherit BaseClass(); string2 = str2 }
let obj1 = DerivedClass("A", "B")
let obj2 = DerivedClass("A")
Alternatives à l'héritage
Dans les cas où une modification mineure d'un type est requise, envisagez d'utiliser une expression d'objet plutôt que l'héritage. L'exemple suivant illustre l'utilisation d'une expression d'objet à la place de la création d'un type dérivé :
open System
let object1 = { new Object() with
override this.ToString() = "This overrides object.ToString()"
}
printfn "%s" (object1.ToString())
Pour plus d'informations sur les expressions d'objet, consultez Expressions d'objet (F#).
Lorsque vous créez des hiérarchies d'objets, envisagez d'utiliser une union discriminée plutôt que l'héritage. Les unions discriminées peuvent également servir de modèle au comportement varié de différent objets qui ont un type global en commun. Une union discriminée unique peut souvent éliminer la nécessité d'utiliser plusieurs classes dérivées qui sont des variations mineures de chacune d'elles. Pour plus d'informations sur les unions discriminées, consultez Unions discriminées (F#).