Generics

F#-Funktionswerte, -Methoden, -Eigenschaften und -Aggregattypen, wie z.B. Klassen und Unterscheidungs-Unions können generisch sein. Generische Konstrukte enthalten mindestens einen Typparameter, die in der Regel vom Benutzer des generischen Konstrukts angegeben wird. Mit generischen Funktionen und Typen können Sie Code schreiben, der mit einer Vielzahl von Typen funktioniert, ohne den Code für jeden Typ zu wiederholen. Ihren Code generisch zu erstellen kann einfach sein, da Ihr Code häufig implizit abgeleitet wird, um durch den Typrückschluss des Compilers und durch automatische Verallgemeinerungsmechanismen generisch zu sein.

Syntax

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body

// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body

// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

Bemerkungen

Die Deklaration einer explizit generischen Funktion oder eines Typs entspricht weitgehend einer nicht generischen Funktion oder des Typs, mit Ausnahme der Spezifikation (und Verwendung) der Typparameter in spitzen Klammern nach dem Namen der Funktion oder des Typs.

Deklarationen sind oft implizit generisch. Wenn Sie den Typ jedes Parameters nicht vollständig angeben, der zum Erstellen einer Funktion oder eines Typs verwendet wird, versucht der Compiler den Typ jedes einzelnen Parameters, Werts und jeder Variablen aus dem Code, den Sie schreiben, abzuleiten. Weitere Informationen finden Sie unter Type Inference (F#). Wenn der Code für Ihren Typ oder die Funktion die Typen der Parameter nicht anderweitig einschränkt, ist die Funktion oder der Typ implizit generisch. Dieser Prozess heißt automatische Verallgemeinerung. Es gibt einige Einschränkungen für die automatische Verallgemeinerung. Wenn beispielsweise der F#-Compiler die Typen für ein generisches Konstrukt nicht ableiten kann, meldet der Compiler einen Fehler, der sich auf eine Einschränkung namens Wertebeschränkung bezieht. In diesem Fall müssen Sie möglicherweise einige Typanmerkungen hinzufügen. Weitere Informationen über die automatische Verallgemeinerung und die Wertebeschränkung, und wie Sie den Code ändern, um das Problem zu lösen, finden Sie unter Automatische Verallgemeinerung.

In der vorherigen Syntax ist type-parameters eine durch Trennzeichen getrennte Liste von Parametern, die unbekannte Typen darstellen, jeweils beginnend mit einem einfachen Anführungszeichen und optional mit einer Einschränkungsklausel, die weiter begrenzt, welche Typen für den Typparameter verwendet werden können. Die Syntax für Einschränkungsklauseln verschiedener Arten und andere Informationen zu Einschränkungen finden Sie unter Einschränkungen.

Die type-definition in der Syntax entspricht der Typdefinition für einen nicht generischen Typ. Sie enthält die Konstruktorparameter für einen Klassentyp, eine optionale as-Klausel, das gleiche Symbol, die Datensatzfelder, die inherit-Klausel, die Optionen für eine Unterscheidungs-Union, let- und do-Bindungen, die Memberdefinitionen und alles andere, was in einer nicht generischen Typdefinition zulässig ist.

Die anderen Syntaxelemente sind identisch mit denen für nicht generische Funktionen und Typen. Beispielsweise ist object-identifier ein Bezeichner, der das enthaltende Objekt selbst darstellt.

Eigenschaften, Felder und Konstruktoren können nicht generischer als der einschließende Typ sein. Außerdem können Werte in einem Modul nicht generisch sein.

Implizit generische Konstrukte

Wenn der F#-Compiler die Typen im Code ableitet, behandelt es automatisch alle Funktionen, die als Generische generisch sein können. Wenn Sie einen Typ explizit angeben, wie z.B. einen Parametertyp, verhindern Sie die automatische Verallgemeinerung.

Im folgenden Codebeispiel ist makeList generisch, obwohl weder es noch seine Parameter explizit als generisch deklariert werden.

let makeList a b =
    [a; b]

Die Signatur der Funktion wird als 'a -> 'a -> 'a list abgeleitet. Beachten Sie, dass a und b in diesem Beispiel abgeleitet werden, um über den gleichen Typ zu verfügen. Dies ist, da sie zusammen in einer Liste enthalten sind, und alle Elemente einer Liste müssen vom gleichen Typ sein.

Sie können auch eine Funktion generisch erstellen, indem Sie die Syntax für einfache Anführungszeichen in einer Typanmerkung verwenden, um anzugeben, dass ein Parametertyp ein generischer Typparameter ist. Im folgenden Code ist function1 generisch, da seine Parameter auf diese Weise als Typparameter deklariert werden.

let function1 (x: 'a) (y: 'a) =
    printfn "%A %A" x y

Explizit generische Konstrukte

Sie können eine Funktion auch generisch erstellen, indem Sie seine Typparameter explizit in spitzen Klammern (<type-parameter>) deklarieren. Dies wird im folgenden Code veranschaulicht.

let function2<'T> (x: 'T) (y: 'T) =
    printfn "%A, %A" x y

Verwenden von generischen Konstrukten

Wenn Sie generische Funktionen oder Methoden verwenden, müssen Sie möglicherweise nicht die Typargumente angeben. Der Compiler verwendet den Typrückschluss, um die entsprechenden Typargumente abzuleiten. Wenn immer noch eine Mehrdeutigkeit vorliegt, können Sie Typargumente in spitzen Klammern angeben und mehrere Typargumente durch Kommas trennen.

Der folgende Code zeigt die Verwendung der Funktionen, die in den vorherigen Abschnitten definiert sind.

// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
    // The compiler reports a warning:
    function1<int> a b
    // No warning.
    function2<int> a b

Hinweis

Es gibt zwei Möglichkeiten zum Verweisen auf einen generischen Typ anhand des Namens. Beispielsweise sind list<int> und int list zwei Methoden zum Verweisen auf einen generischen Typ list, der über ein einzelnes Typargument int verfügt. Die letztgenannte Form wird konventionell nur mit integrierten F#-Typen verwendet, wie z.B. list und option. Wenn mehrere Typargumente vorhanden sind, verwenden Sie normalerweise die Syntax Dictionary<int, string>, aber Sie können auch die Syntax (int, string) Dictionary verwenden.

Platzhalter als Typargumente

Um anzugeben, dass ein Typargument vom Compiler abgeleitet werden soll, können Sie den Unterstrich bzw. das Platzhaltersymbol (_) anstatt eines benannten Typarguments verwenden. Dies wird im folgenden Code veranschaulicht.

let printSequence (sequence1: Collections.seq<_>) =
   Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1

Einschränkungen bei generischen Typen und Funktionen

In einer generischen Typ- oder Funktionsdefinition können Sie nur diese Konstrukte verwenden, die bekanntermaßen für den generischen Typparameter verfügbar sind. Dies ist erforderlich, um die Überprüfung von Funktions- und Methodenaufrufen zur Kompilierzeit zu aktivieren. Wenn Sie Ihre Typparameter explizit deklarieren, können Sie eine explizite Einschränkung für einen generischen Typparameter anwenden, um den Compiler zu informieren, dass bestimmte Methoden und Funktionen verfügbar sind. Wenn Sie F#-Compiler die generischen Parametertypen ableiten lassen, wird es jedoch die entsprechenden Einschränkungen für Sie bestimmen. Weitere Informationen finden Sie unter Einschränkungen.

Statisch aufgelöste Typparameter

Es gibt zwei Arten von Typparametern, die in F#-Programmen verwendet werden können. Die Ersten sind generische Typparameter der Art, die in den vorherigen Abschnitten beschrieben werden. Diese erste Art von Typparameter entspricht den generischen Typparametern, die in Sprachen wie Visual Basic und C# verwendet werden. Eine andere Art von Typparameter ist für F# spezifisch und wird als ein statisch aufgelöster Typparameter bezeichnet. Informationen zu diesen Konstrukten finden Sie unter Statisch aufgelöste Typparameter.

Beispiele

// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x : 'a) (y : 'a) =
    printf "%A %A" x y

// A generic record, with the type parameter in angle brackets.
type GR<'a> =
    {
        Field1: 'a;
        Field2: 'a;
    }

// A generic class.
type C<'a>(a : 'a, b : 'a) =
    let z = a
    let y = b
    member this.GenericMethod(x : 'a) =
        printfn "%A %A %A" x y z

// A generic discriminated union.
type U<'a> =
    | Choice1 of 'a
    | Choice2 of 'a * 'a

type Test() =
    // A generic member
    member this.Function1<'a>(x, y) =
        printfn "%A, %A" x y

    // A generic abstract method.
    abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
    override this.abstractMethod<'a, 'b>(x:'a, y:'b) =
         printfn "%A, %A" x y

Weitere Informationen