Classes abstraites (F#)
Les classes abstraites sont des classes qui laissent une partie ou la totalité des membres non implémentés, de manière à ce que les implémentations puissent être fournies par des classes dérivées.
// Abstract class syntax.
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
[ inherit base-class-or-interface-name ]
[ abstract-member-declarations-and-member-definitions ]
// Abstract member syntax.
abstract member member-name : type-signature
Notes
Dans la programmation orientée objet, une classe abstraite est utilisée comme classe de base d'une hiérarchie et représente les fonctionnalités communes d'un jeu divers de types d'objet. Comme leur nom l'indique, les classes abstraites ne correspondent pas souvent directement à des entités concrètes dans le domaine de problème. Toutefois, elles représentent ce que de nombreuses entités concrètes ont en commun.
Les classes abstraites doivent avoir l'attribut AbstractClass. Elles peuvent avoir des membres implémentés et non implémentés. L'utilisation du terme abstrait pour désigner une classe est la même que dans d'autres langages .NET ; toutefois, l'utilisation du terme abstrait pour désigner des méthodes (et des propriétés) est quelque peu différente en F#. En F#, lorsqu'une méthode est marquée avec le mot clé abstract, cela indique qu'un membre a une entrée, appelée emplacement de dispatch virtuel, dans la table interne des fonctions virtuelles pour ce type. En d'autres termes, la méthode est virtuelle, bien que le mot clé virtual ne soit pas utilisé dans le langage F#. Le mot clé abstract est utilisé sur les méthodes virtuelles, que la méthode soit implémentée ou non. La déclaration d'un emplacement de dispatch virtuel est séparée de la définition d'une méthode pour cet emplacement de dispatch. Par conséquent, l'équivalent en F# d'une déclaration et d'une définition de méthode virtuelle dans un autre langage .NET est une combinaison d'une déclaration de méthode abstraite et d'une définition séparée, avec le mot clé default ou override. Pour plus d'informations et d'exemples, consultez Méthodes (F#).
Une classe est considérée comme abstraite uniquement si des méthodes abstraites sont déclarées et non définies. Par conséquent, les classes qui ont des méthodes abstraites ne sont pas nécessairement des classes abstraites. À moins qu'une classe ait des méthodes abstraites non définies, n'utilisez pas l'attribut AbstractClass.
Dans la syntaxe précédente, accessibility-modifier peut être public, private ou internal. Pour plus d'informations, consultez Contrôle d'accès (F#).
Comme avec d'autres types, les classes abstraites peuvent avoir une classe de base et une ou plusieurs interfaces de base. Chaque classe ou interface de base apparaît sur une ligne distincte avec le mot clé inherit.
La définition de type d'une classe abstraite peut contenir des membres entièrement définis, mais elle peut également contenir des membres abstraits. La syntaxe pour les membres abstraits est indiquée séparément dans la syntaxe précédente. Dans cette syntaxe, la type signature d'un membre est une liste qui contient les types de paramètre dans l'ordre et les types de retour, séparés par des jetons -> et/ou des jetons * selon qu'il s'agit de paramètres curryfiés ou basés sur des tuples. La syntaxe pour les signatures de type de membre abstrait est la même que celle utilisée dans les fichiers de signature et que celle indiquée par IntelliSense dans l'Éditeur de code Visual Studio.
Exemple
Le code suivant illustre une classe abstraite Shape ayant deux classes dérivées abstraites : Square et Circle. L'exemple suivant montre comment utiliser des classes, des méthodes et des propriétés abstraites. Dans l'exemple, le classe abstraite Shape représente les éléments communs des entités concrètes circle et square. Les fonctionnalités communes à toutes les formes (dans un système de coordonnées à deux dimensions) sont abstraites dans la classe Shape : la position sur la grille, un angle de rotation et les propriétés de zone et de périmètre. Celles-ci peuvent être substituées, à l'exception de la position, dont les formes individuelles ne peuvent pas modifier le comportement.
La méthode de rotation peut être substituée, comme dans la classe Circle, qui est indifférente à la rotation en raison de sa symétrie. Ainsi, dans la classe Circle, la méthode de rotation est remplacée par une méthode qui ne fait rien.
// An abstract class that has some methods and properties defined
// and some left abstract.
[<AbstractClass>]
type Shape2D(x0 : float, y0 : float) =
let mutable x, y = x0, y0
let mutable rotAngle = 0.0
// These properties are not declared abstract. They
// cannot be overriden.
member this.CenterX with get() = x and set xval = x <- xval
member this.CenterY with get() = y and set yval = y <- yval
// These properties are abstract, and no default implementation
// is provided. Non-abstract derived classes must implement these.
abstract Area : float with get
abstract Perimeter : float with get
abstract Name : string with get
// This method is not declared abstract. It cannot be
// overriden.
member this.Move dx dy =
x <- x + dx
y <- y + dy
// An abstract method that is given a default implementation
// is equivalent to a virtual method in other .NET languages.
// Rotate changes the internal angle of rotation of the square.
// Angle is assumed to be in degrees.
abstract member Rotate: float -> unit
default this.Rotate(angle) = rotAngle <- rotAngle + angle
type Square(x, y, sideLengthIn) =
inherit Shape2D(x, y)
member this.SideLength = sideLengthIn
override this.Area = this.SideLength * this.SideLength
override this.Perimeter = this.SideLength * 4.
override this.Name = "Square"
type Circle(x, y, radius) =
inherit Shape2D(x, y)
let PI = 3.141592654
member this.Radius = radius
override this.Area = PI * this.Radius * this.Radius
override this.Perimeter = 2. * PI * this.Radius
// Rotating a circle does nothing, so use the wildcard
// character to discard the unused argument and
// evaluate to unit.
override this.Rotate(_) = ()
override this.Name = "Circle"
let square1 = new Square(0.0, 0.0, 10.0)
let circle1 = new Circle(0.0, 0.0, 5.0)
circle1.CenterX <- 1.0
circle1.CenterY <- -2.0
square1.Move -1.0 2.0
square1.Rotate 45.0
circle1.Rotate 45.0
printfn "Perimeter of square with side length %f is %f, %f"
(square1.SideLength) (square1.Area) (square1.Perimeter)
printfn "Circumference of circle with radius %f is %f, %f"
(circle1.Radius) (circle1.Area) (circle1.Perimeter)
let shapeList : list<Shape2D> = [ (square1 :> Shape2D);
(circle1 :> Shape2D) ]
List.iter (fun (elem : Shape2D) ->
printfn "Area of %s: %f" (elem.Name) (elem.Area))
shapeList