classes abstraites

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.

Syntaxe

// 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 courantes d’un ensemble diversifié de types d’objets. Comme l’indique le nom « abstrait », les classes abstraites ne correspondent souvent pas directement aux entités concrètes dans le domaine du problème. Elles représentent toutefois ce que de nombreuses entités concrètes ont en commun.

Les classes abstraites doivent présenter l’attribut AbstractClass. Elles peuvent disposer de membres implémentés et non implémentés. L’utilisation du terme abstrait lorsqu’il est appliqué à une classe est la même que dans d’autres langages .NET. Toutefois, l’utilisation du terme abstrait lorsqu’il est appliqué à des méthodes (et des propriétés) diffère légèrement dans F# de son utilisation dans d’autres langages .NET. Dans F#, lorsqu’une méthode est marquée avec le mot clé abstract, cela indique qu’un membre dispose d’une entrée, appelée emplacement de dispatch virtuel, dans le tableau interne des fonctions virtuelles pour ce type. En d’autres termes, la méthode est virtuelle, même si le mot clé virtual n’est pas utilisé dans F#. Le mot clé abstract est utilisé dans les méthodes virtuelles, que la méthode soit implémentée ou non. La déclaration d’un emplacement de dispatch virtuel est indépendante de la définition d’une méthode pour cet emplacement de dispatch. Par conséquent, l’équivalent F# d’une déclaration et d’une définition de méthode virtuelle dans un autre langage .NET est la combinaison d’une déclaration de méthode abstraite et d’une définition distincte, avec le mot clé default ou le mot cléoverride. Pour plus d’informations et d’exemples, consultez Méthodes.

Une classe est considérée comme abstraite uniquement s’il existe des méthodes abstraites déclarées, mais non définies. Par conséquent, les classes qui présentent des méthodes abstraites ne sont pas nécessairement des classes abstraites. À moins qu’une classe présente des méthodes abstraites non définies, n’utilisez pas l’attribut AbstractClass.

Dans la syntaxe précédente, le modificateur d’accessibilité peut être public, private ou internal. Pour obtenir plus d'informations, voir Contrôle d'accès.

Comme pour d’autres types, les classes abstraites peuvent présenter une classe de base et une ou plusieurs interfaces de base. Chaque classe de base ou interface 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 aussi contenir des membres abstraits. La syntaxe des membres abstraits s’affiche séparément dans la syntaxe précédente. Dans cette syntaxe, la signature de type d’un membre est une liste qui contient les types de paramètres dans l’ordre et les types de retour, séparés par des jetons -> et/ou des jetons * appropriés pour les paramètres curryfiés et tuplés. La syntaxe des signatures de type membre abstraites est la même que celle utilisée dans les fichiers de signature et affichée par IntelliSense dans Visual Studio Code Editor.

Le code suivant illustre une forme de classe abstraite, qui présente deux classes dérivées non abstraites, Carré et Cercle. L’exemple montre comment utiliser des classes abstraites, des méthodes et des propriétés. Dans l’exemple, la forme de classe abstraite représente les éléments communs des entités concrètes Cercle et Carré. Les caractéristiques courantes de toutes les formes (dans un système de coordonnées à deux dimensions) sont abstraites dans la classe de forme : la position sur la grille, un angle de rotation et les propriétés de zone et de périmètre. Elles peuvent être remplacées, à l’exception de la position et du comportement dont les formes individuelles ne peuvent pas être modifiées.

La méthode de rotation peut être remplacée, comme dans la classe Cercle, qui est invariante par rotation en raison de sa symétrie. Ainsi, dans la classe Cercle, 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
    // overridden.
    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

Output:

Perimeter of square with side length 10.000000 is 40.000000
Circumference of circle with radius 5.000000 is 31.415927
Area of Square: 100.000000
Area of Circle: 78.539816

Voir aussi