Abstracte klassen

Abstracte klassen zijn klassen die sommige of alle leden ongemplementeerd laten, zodat implementaties kunnen worden aangeboden door afgeleide klassen.

Syntaxis

// 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

Opmerkingen

In objectgeoriënteerde programmering wordt een abstracte klasse gebruikt als een basisklasse van een hiërarchie en vertegenwoordigt het algemene functionaliteit van een diverse set objecttypen. Zoals de naam 'abstract' impliceert, komen abstracte klassen vaak niet rechtstreeks overeen met concrete entiteiten in het probleemdomein. Ze vertegenwoordigen echter wel wat veel verschillende concrete entiteiten gemeen hebben.

Abstracte klassen moeten het AbstractClass kenmerk hebben. Ze kunnen geïmplementeerde en niet-geïmplementeerde leden hebben. Het gebruik van de termabstractie die wordt toegepast op een klasse, is hetzelfde als in andere .NET-talen. Het gebruik van de termabstractie op methoden (en eigenschappen) is echter iets anders in F# dan in andere .NET-talen. Als in F# een methode is gemarkeerd met het abstract trefwoord, geeft dit aan dat een lid een vermelding heeft, ook wel een virtuele dispatchsite genoemd, in de interne tabel met virtuele functies voor dat type. Met andere woorden, de methode is virtueel, hoewel het virtual trefwoord niet wordt gebruikt in F#. Het trefwoord abstract wordt gebruikt voor virtuele methoden, ongeacht of de methode wordt geïmplementeerd. De declaratie van een virtuele verzendsite is gescheiden van de definitie van een methode voor die verzendsite. Daarom is het F#-equivalent van een declaratie en definitie van een virtuele methode in een andere .NET-taal een combinatie van zowel een abstracte methodedeclaratie als een afzonderlijke definitie, met het default trefwoord of het override trefwoord. Zie Methoden voor meer informatie en voorbeelden.

Een klasse wordt alleen als abstract beschouwd als er abstracte methoden zijn die zijn gedeclareerd maar niet gedefinieerd. Daarom zijn klassen met abstracte methoden niet noodzakelijkerwijs abstracte klassen. Gebruik het kenmerk AbstractClass alleen als een klasse niet-gedefinieerde abstracte methoden heeft.

In de vorige syntaxis kan toegankelijkheidsaanpassing worden publicgebruikt, private of internal. Zie Toegangsbeheer voor meer informatie.

Net als bij andere typen kunnen abstracte klassen een basisklasse en een of meer basisinterfaces hebben. Elke basisklasse of interface wordt samen met het inherit trefwoord weergegeven op een afzonderlijke regel.

De typedefinitie van een abstracte klasse kan volledig gedefinieerde leden bevatten, maar kan ook abstracte leden bevatten. De syntaxis voor abstracte leden wordt afzonderlijk weergegeven in de vorige syntaxis. In deze syntaxis is de typehandtekening van een lid een lijst met de parametertypen in volgorde en de retourtypen, gescheiden door -> tokens en/of * tokens, indien geschikt voor gecureerde en tupled parameters. De syntaxis voor abstracte handtekeningtypen voor leden is hetzelfde als de syntaxis die wordt gebruikt in handtekeningbestanden en die worden weergegeven door IntelliSense in de Visual Studio Code-editor.

De volgende code illustreert een abstracte klasse Shape, die twee niet-abstracte afgeleide klassen heeft, Vierkant en Cirkel. In het voorbeeld ziet u hoe u abstracte klassen, methoden en eigenschappen gebruikt. In het voorbeeld vertegenwoordigt de abstracte klasse Shape de gemeenschappelijke elementen van de cirkel en het vierkant van de betonentiteiten. De algemene kenmerken van alle vormen (in een tweedimensionaal coördinaatsysteem) worden geabstraheerd in de klasse Shape: de positie in het raster, een draaihoek en de vlak- en perimetereigenschappen. Deze kunnen worden overschreven, met uitzondering van positie, het gedrag waarvan afzonderlijke shapes niet kunnen worden gewijzigd.

De rotatiemethode kan worden overschreven, zoals in de circle-klasse, die invariant roteert vanwege de symmetrie. In de circle-klasse wordt de rotatiemethode dus vervangen door een methode die niets doet.

// 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

Uitvoer:

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

Zie ook