Methoden

Eine Methode ist eine Funktion, die einem Typ zugeordnet ist. Bei objektorientierter Programmierung werden Methoden verwendet, um die Funktionalität und das Verhalten von Objekten und Typen verfügbar zu machen und zu implementieren.

Syntax

// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Static method definition.
[ attributes ]
static member [inline] method-name parameter-list [ : return-type ] =
    method-body

// Abstract method declaration or virtual dispatch slot.
[ attributes ]
abstract member method-name : type-signature

// Virtual method declaration and default implementation.
[ attributes ]
abstract member method-name : type-signature
[ attributes ]
default self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Override of inherited virtual method.
[ attributes ]
override self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Optional and DefaultParameterValue attributes on input parameters
[ attributes ]
[ modifier ] member [inline] self-identifier.method-name ([<Optional; DefaultParameterValue( default-value )>] input) [ : return-type ]

Bemerkungen

In der Syntax oben können Sie die verschiedenen Formen von Methodendeklarationen und -definitionen sehen. In längeren Methodentexten folgt ein Zeilenumbruch auf das Gleichheitszeichen (=), und der gesamte Methodentext wird eingezogen.

Attribute können auf jede Methodendeklaration angewendet werden. Sie gehen der Syntax für eine Methodendefinition voran und werden normalerweise in einer separaten Zeile aufgeführt. Weitere Informationen finden Sie unter Attribute.

Methoden können als inline markiert werden. Informationen zu inline finden Sie unter Inlinefunktionen.

Nicht-Inlinemethoden können rekursiv innerhalb des Typs verwendet werden. Es ist nicht erforderlich, das Schlüsselwort rec explizit zu verwenden.

Instanzmethoden

Instanzmethoden werden mit dem Schlüsselwort member und einem Selbstbezeichner, gefolgt von einem Punkt (.) und dem Methodennamen und den Parametern deklariert. Wie bei let-Bindungen kann die parameter-list ein Muster sein. In der Regel schließen Sie Methodenparameter in Klammern in einer Tupelform ein. Dies ist die Art und Weise, wie Methoden in F# verwendet werden, wenn sie in anderen Sprachen von .NET Framework erstellt werden. Allerdings ist auch die Curried-Form (durch Leerzeichen getrennte Parameter) üblich, und auch andere Muster werden unterstützt.

Das folgende Beispiel zeigt die Definition und Verwendung einer nicht abstrakten instanzmethode.

type SomeType(factor0: int) =
    let factor = factor0
    member this.SomeMethod(a, b, c) = (a + b + c) * factor

    member this.SomeOtherMethod(a, b, c) = this.SomeMethod(a, b, c) * factor

Verwenden Sie innerhalb von Instanzmethoden nicht den Selbstbezeichner, um auf Felder zuzugreifen, die mithilfe von let-Bindungen definiert wurden. Verwenden Sie den Selbstbezeichner beim Zugriff auf andere Member und Eigenschaften.

Statische Methoden

Die Schlüsselwort static wird verwendet, um anzugeben, dass eine Methode ohne eine Instanz aufgerufen werden kann und keiner Objektinstanz zugeordnet ist. Andernfalls sind Methoden Instanzmethoden.

Das Beispiel im nächsten Abschnitt zeigt Felder, die mit dem Schlüsselwort let deklariert wurden, Eigenschaftenmember, die mit dem Schlüsselwort member deklariert wurden, und eine statische Methode, die mit dem Schlüsselwort static deklariert wurde.

Das folgende Beispiel zeigt die Definition und Verwendung statischer Methoden. Angenommen, diese Methodendefinitionen befinden sich im vorherigen Abschnitt in der SomeType-Klasse.

static member SomeStaticMethod(a, b, c) =
   (a + b + c)

static member SomeOtherStaticMethod(a, b, c) =
   SomeType.SomeStaticMethod(a, b, c) * 100

Abstrakte und virtuelle Methoden

Die Schlüsselwort abstract gibt an, dass eine Methode über einen virtuellen Dispatchslot verfügt und möglicherweise keine Definition in der Klasse aufweist. Ein virtueller Dispatchslot ist ein Eintrag in einer intern verwalteten Funktionstabelle, der zur Laufzeit verwendet wird, um virtuelle Funktionsaufrufe in einem objektorientierten Typ nachzuschlagen. Der virtuelle Dispatchmechanismus ist der Mechanismus, der Polymorphismus implementiert, ein wichtiges Feature objektorientierter Programmierung. Eine Klasse, die über mindestens eine abstrakte Methode ohne Definition verfügt, ist eine abstrakte Klasse, was bedeutet, dass keine Instanzen dieser Klasse erstellt werden können. Weitere Informationen zu abstrakten Klassen finden Sie unter Abstrakte Klassen.

Abstrakte Methodendeklarationen enthalten keinen Methodentext. Stattdessen folgt auf den Namen der Methode ein Doppelpunkt (:) und eine Typsignatur für die Methode. Die Typsignatur einer Methode ist mit der von IntelliSense angezeigten Signatur identisch, wenn Sie den Mauszeiger im Visual Studio Code-Editor auf einem Methodennamen positionieren, ausgenommen ohne Parameternamen. Typsignaturen werden auch vom Interpreter („fsi.exe“) angezeigt, wenn Sie interaktiv arbeiten. Die Typsignatur einer Methode wird generiert, indem die Typen der Parameter mit entsprechenden Trennzeichensymbolen aufgelistet werden, gefolgt vom Rückgabetyp. Curried-Parameter werden durch -> und Tupelparameter durch * getrennt. Der Rückgabewert wird immer durch ein ->-Symbol von den Argumenten getrennt. Klammern können verwendet werden, um komplexe Parameter zu gruppieren, z. B. wenn ein Funktionstyp ein Parameter ist, oder um anzugeben, wann ein Tupel als einzelner Parameter und nicht als zwei Parameter behandelt wird.

Sie können abstrakte Methoden auch mit Standarddefinitionen versehen, indem Sie die Definition der Klasse hinzufügen und das Schlüsselwort default verwenden, wie im Syntaxblock in diesem Thema gezeigt. Eine abstrakte Methode, die über eine Definition in derselben Klasse verfügt, entspricht einer virtuellen Methode in anderen .NET Framework-Sprachen. Unabhängig davon, ob eine Definition vorhanden ist, erstellt das Schlüsselwort abstract einen neuen Dispatchslot in der virtuellen Funktionstabelle für die Klasse.

Unabhängig davon, ob eine Basisklasse ihre abstrakten Methoden implementiert, können abgeleitete Klassen Implementierungen abstrakter Methoden bereitstellen. Um eine abstrakte Methode in einer abgeleiteten Klasse zu implementieren, definieren Sie eine Methode, die denselben Namen und dieselbe Signatur in der abgeleiteten Klasse aufweist. Verwenden Sie jedoch das Schlüsselwort override oder default, und geben Sie den Methodentext an. Die Schlüsselwörter override und default bedeuten genau das Gleiche. Verwenden Sie override, wenn die neue Methode eine Basisklassenimplementierung außer Kraft setzt. Verwenden Sie default, wenn Sie eine Implementierung in derselben Klasse wie die ursprüngliche abstrakte Deklaration erstellen. Verwenden Sie das Schlüsselwort abstract nicht für die Methode, die die Methode implementiert, die in der Basisklasse als abstrakt deklariert wurde.

Das folgende Beispiel zeigt eine abstrakte Methode Rotate, die eine Standardimplementierung aufweist, die einer virtuellen Methode von .NET Framework entspricht.

type Ellipse(a0: float, b0: float, theta0: float) =
    let mutable axis1 = a0
    let mutable axis2 = b0
    let mutable rotAngle = theta0
    abstract member Rotate: float -> unit
    default this.Rotate(delta: float) = rotAngle <- rotAngle + delta

Das folgende Beispiel zeigt eine abgeleitete Klasse, die eine Basisklassenmethode außer Kraft setzt. In diesem Fall ändert die Überschreibung das Verhalten, sodass die Methode keine Aktion ausführt.

type Circle(radius: float) =
    inherit Ellipse(radius, radius, 0.0)
    // Circles are invariant to rotation, so do nothing.
    override this.Rotate(_) = ()

Überladene Methoden

Überladene Methoden sind Methoden, die identische Namen in einem bestimmten Typ aufweisen, aber unterschiedliche Argumente besitzen. In F# werden in der Regel optionale Argumente anstelle von überladenen Methoden verwendet. Überladene Methoden sind jedoch in der Sprache zulässig, vorausgesetzt, die Argumente liegen in Tupelform und nicht in Curried-Form vor.

Optionale Argumente

Ab F# 4.1 können Sie in Methoden auch optionale Argumente mit einem Standardwert verwenden. Dies soll die Zusammenarbeit mit C#-Code erleichtern. Das folgende Beispiel zeigt die Syntax:

open System.Runtime.InteropServices
// A class with a method M, which takes in an optional integer argument.
type C() =
    member _.M([<Optional; DefaultParameterValue(12)>] i) = i + 1

Beachten Sie, dass der für DefaultParameterValue übergebene Wert mit dem Eingabetyp übereinstimmen muss. Im Beispiel oben ist es ein int-Wert. Der Versuch, einen nicht ganzzahligen Wert an DefaultParameterValue zu übergeben, führt zu einem Kompilierungsfehler.

Beispiel: Eigenschaften und Methoden

Das folgende Beispiel enthält einen Typ mit Beispielen für Felder, private Funktionen, Eigenschaften und eine statische Methode.

type RectangleXY(x1: float, y1: float, x2: float, y2: float) =
    // Field definitions.
    let height = y2 - y1
    let width = x2 - x1
    let area = height * width
    // Private functions.
    static let maxFloat (x: float) (y: float) = if x >= y then x else y
    static let minFloat (x: float) (y: float) = if x <= y then x else y
    // Properties.
    // Here, "this" is used as the self identifier,
    // but it can be any identifier.
    member this.X1 = x1
    member this.Y1 = y1
    member this.X2 = x2
    member this.Y2 = y2
    // A static method.
    static member intersection(rect1: RectangleXY, rect2: RectangleXY) =
        let x1 = maxFloat rect1.X1 rect2.X1
        let y1 = maxFloat rect1.Y1 rect2.Y1
        let x2 = minFloat rect1.X2 rect2.X2
        let y2 = minFloat rect1.Y2 rect2.Y2

        let result: RectangleXY option =
            if (x2 > x1 && y2 > y1) then
                Some(RectangleXY(x1, y1, x2, y2))
            else
                None

        result

// Test code.
let testIntersection =
    let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
    let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
    let r3: RectangleXY option = RectangleXY.intersection (r1, r2)

    match r3 with
    | Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
    | None -> printfn "No intersection found."

testIntersection

Weitere Informationen