Dela via


Abstrakta klasser

Abstrakta klasser är klasser som lämnar vissa eller alla medlemmar ogenomförda, så att implementeringar kan tillhandahållas av härledda klasser.

Syntax

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

Kommentarer

I objektorienterad programmering används en abstrakt klass som en basklass i en hierarki och representerar vanliga funktioner i en mängd olika typer av objekt. Som namnet "abstrakt" antyder motsvarar abstrakta klasser ofta inte direkt på konkreta entiteter i problemdomänen. De representerar dock vad många olika konkreta entiteter har gemensamt.

Abstrakta klasser måste ha attributet AbstractClass . De kan ha implementerat och ogenomfört medlemmar. Användningen av termen abstrakt när den tillämpas på en klass är densamma som i andra .NET-språk, men användningen av termen abstrakt när den tillämpas på metoder (och egenskaper) skiljer sig lite i F# från dess användning på andra .NET-språk. När en metod markeras med nyckelordet abstract I F# anger detta att en medlem har en post, känd som ett virtuellt sändningsfack, i den interna tabellen med virtuella funktioner för den typen. Med andra ord är metoden virtuell, även om nyckelordet virtual inte används i F#. Nyckelordet abstract används på virtuella metoder oavsett om metoden implementeras. Deklarationen av ett virtuellt sändningsfack är separat från definitionen av en metod för det sändningsfacket. Därför är F#-motsvarigheten till en virtuell metoddeklaration och definition på ett annat .NET-språk en kombination av både en abstrakt metoddeklaration och en separat definition, med antingen nyckelordet default eller nyckelordet override . Mer information och exempel finns i Metoder.

En klass betraktas endast som abstrakt om det finns abstrakta metoder som deklareras men inte definieras. Därför är klasser som har abstrakta metoder inte nödvändigtvis abstrakta klasser. Om inte en klass har odefinierade abstrakta metoder ska du inte använda attributet AbstractClass .

I föregående syntax kan hjälpmedelsmodifieraren vara public, private eller internal. Mer information finns i Åtkomstkontroll.

Precis som med andra typer kan abstrakta klasser ha en basklass och ett eller flera basgränssnitt. Varje basklass eller gränssnitt visas på en separat rad tillsammans med nyckelordet inherit .

Typdefinitionen för en abstrakt klass kan innehålla fullständigt definierade medlemmar, men den kan också innehålla abstrakta medlemmar. Syntaxen för abstrakta medlemmar visas separat i föregående syntax. I den här syntaxen är typsignaturen för en medlem en lista som innehåller parametertyperna i ordning och returtyperna, avgränsade med -> token och/eller * token efter behov för curryparametrar och tupled-parametrar. Syntaxen för abstrakta medlemstypssignaturer är densamma som den som används i signaturfiler och som visas av IntelliSense i Visual Studio Code-redigeraren.

Följande kod illustrerar en abstrakt klassform, som har två icke-abstrakta härledda klasser, Kvadrat och Cirkel. Exemplet visar hur du använder abstrakta klasser, metoder och egenskaper. I exemplet representerar den abstrakta klassen Shape de gemensamma elementen i de konkreta entiteterna cirkel och kvadrat. De vanliga funktionerna i alla former (i ett tvådimensionellt koordinatsystem) abstraheras till klassen Shape: positionen på rutnätet, rotationsvinkeln och området och perimeteregenskaperna. Dessa kan åsidosättas, förutom position, beteendet för vilka enskilda former inte kan ändras.

Rotationsmetoden kan åsidosättas, som i klassen Circle, som är rotationsvariant på grund av dess symmetri. Så i klassen Circle ersätts rotationsmetoden med en metod som inte gör någonting.

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

Produktionen:

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

Se även