Abstrakte Klassen (F#)

Abstrakte Klassen sind Klassen, in denen einige oder alle Member unimplementiert bleiben, sodass Implementierungen von abgeleiteten Klassen bereitgestellt werden können.

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

Hinweise

Bei der objektorientierten Programmierung wird eine abstrakte Klasse als Basisklasse einer Hierarchie verwendet, und sie stellt die allgemeine Funktionalität eines Satzes unterschiedlicher Objekttypen dar. Wie der Begriff "abstrakt" besagt, entsprechen abstrakte Klassen häufig nicht direkt konkreten Entitäten im Problembereich. Sie stellen jedoch dar, was viele unterschiedliche konkrete Entitäten gemeinsam haben.

Abstrakte Klassen müssen über das AbstractClass-Attribut verfügen. Sie können über implementierte und über nicht implementierte Member verfügen. Der Begriff abstrakt für eine Klasse bedeutet dasselbe wie in anderen .NET-Sprachen. Wenn jedoch der Begriff abstrakt für Methoden (und Eigenschaften) verwendet wird, unterscheidet sich seine Bedeutung in F# ein wenig von der Bedeutung in anderen .NET-Sprachen. Wenn in F# eine Methode mit dem abstract-Schlüsselwort markiert ist, bedeutet dies, dass ein Member über einen als virtueller Dispatchslot bezeichneten Eintrag in der internen Tabelle virtueller Funktionen für diesen Typ verfügt. Mit anderen Worten, die Methode ist virtuell, obwohl in F# das virtual-Schlüsselwort nicht verwendet wird. Das Schlüsselwort abstract wird für virtuelle Methoden unabhängig davon verwendet, ob die Methode implementiert ist. Die Deklaration eines virtuellen Dispatchslots ist nicht mit der Definition einer Methode für den Dispatchslot identisch. Daher ist in F# das Äquivalent für die Deklaration und Definition einer virtuellen Methode in einer anderen .NET-Sprache eine Kombination einer abstrakten Methodendeklaration und einer separaten Definition mit dem default-Schlüsselwort oder dem override-Schlüsselwort. Weitere Informationen und Beispiele finden Sie unter Methoden (F#).

Eine Klasse gilt nur dann als abstrakt, wenn abstrakte Methoden vorhanden sind, die deklariert, jedoch nicht definiert wurden. Daher sind Klassen, die über abstrakte Methoden verfügen, nicht zwangsläufig abstrakte Klassen. Verwenden Sie das AbstractClass-Attribut nur, wenn eine Klasse über nicht definierte abstrakte Methoden verfügt.

In der vorherigen Syntax kann der accessibility-modifier public, private oder internal lauten. Weitere Informationen finden Sie unter Zugriffssteuerung (F#).

Abstrakte Klassen können wie andere Typen über eine Basisklasse und eine oder mehrere Basisschnittstellen verfügen. Jede Basisklasse oder -schnittstelle befindet sich zusammen mit dem inherit-Schlüsselwort in einer eigenen Zeile.

Die Typdefinition einer abstrakten Klasse kann vollständig definierte Member enthalten, sie kann jedoch auch abstrakte Member enthalten. Die Syntax für abstrakte Member wird in der vorherigen Syntax gesondert dargestellt. In dieser Syntax ist die type signature eines Members eine Liste, die die Parametertypen einzeln nacheinander sowie die Rückgabetypen enthält, durch ->-Token und/oder *-Token getrennt, je nachdem, ob es sich um Curry-Parameter oder um Tupelparameter handelt. Die Syntax der Typsignaturen für abstrakte Member ist mit der Syntax in Signaturdateien und der von IntelliSense im Code-Editor von Visual Studio angezeigten Syntax identisch.

Beispiel

Im folgenden Code wird die abstrakte Klasse Shape veranschaulicht, die über die beiden nicht abstrakten abgeleiteten Klassen Square und Circle verfügt. In dem Beispiel wird gezeigt, wie abstrakte Klassen, Methoden und Eigenschaften verwendet werden. Im Beispiel stellt die abstrakte Klasse Shape die gemeinsamen Elemente der konkreten Entitäten Kreis und Quadrat dar. Die gemeinsamen Funktionen aller Formen (in einem zweidimensionalen Koordinatensystem) werden in der Shape-Klasse abstrahiert: die Position im Raster, ein Drehungswinkel sowie die Eigenschaften von Fläche und Umkreis. Diese können überschrieben werden, mit Ausnahme der Position, deren Verhalten nicht durch einzelne Formen geändert werden kann.

Die Drehungsmethode kann überschrieben werden, da sie sich in der Circle-Klasse befindet, die aufgrund ihrer Symmetrie drehungsunabhängig ist. Deshalb wird die Drehungsmethode in der Circle-Klasse durch eine Methode ersetzt, die keine Aktion ausführt.

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

Siehe auch

Referenz

Klassen (F#)

Methoden (F#)

Eigenschaften (F#)

Konzepte

Member (F#)