Generika (F#)
Funktionswerte, Methoden, Eigenschaften und aggregierte Typen z. B. Klassen, Datensätze und Unterscheidungs-Unions in F# können generisch sein. Generische Konstrukte enthalten mindestens einen Typparameter, der normalerweise vom Benutzer des generischen Konstrukts angegeben wird. Generische Funktionen und Typen ermöglichen das Schreiben von Code, der mit einer Vielzahl von Typen funktioniert. Dabei muss der Code für die einzelnen Typen nicht wiederholt werden. Das Erstellen eines generischen Codes in F# ist ganz einfach, da vom Typrückschluss des Compilers und von automatischen Verallgemeinerungsmechanismen oft implizit gefolgert wird, dass der Code generisch ist.
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifer.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
Hinweise
Die Deklaration einer explizit generischen Funktion oder eines Typs entspricht bis auf die Spezifikation und Verwendung der Typparameter (in spitzen Klammern nach der Funktion oder dem Typnamen) der Deklaration einer nicht generischen Funktion bzw. eines nicht generischen Typs.
Deklarationen sind oft implizit generisch. Werden die Typen der Parameter zum Verfassen einer Funktion oder eines Typs nicht vollständig angeben, versucht der Compiler, die Typen der Parameter, Werte und Variablen vom geschriebenen Code per Rückschluss abzuleiten. Weitere Informationen finden Sie unter Typrückschluss (F#). Werden im Code für den Typ oder die Funktion die Parametertypen nicht eingeschränkt, ist die Funktion bzw. der Typ implizit generisch. Dieser Prozess wird automatische Verallgemeinerung genannt. Für die automatische Verallgemeinerung gelten jedoch einige Einschränkungen. Wenn beispielsweise vom F#-Compiler die Typen eines generischen Konstrukts nicht per Rückschluss abgeleitet werden können, wird eine Fehlermeldung ausgegeben, die auf die Einschränkung Wertbeschränkung verweist. In diesem Fall müssen Sie möglicherweise einige Typanmerkungen hinzufügen. Weitere Informationen zur automatischen Verallgemeinerung, zur Wertbeschränkung und der Änderung des Codes zur Behebung des Problems finden Sie unter Automatische Verallgemeinerung (F#).
In der vorherigen Syntax ist type-parameters eine durch Trennzeichen getrennte Liste von Parametern, die unbekannte Typen darstellen. Jeder der Typen beginnt mit einem einfachen Anführungszeichen und optional mit einer Einschränkungsklausel, die die für diesen Typparameter erlaubten Typen weiter einschränkt. Die Syntax für Einschränkungsklauseln verschiedener Arten und andere Informationen zu Einschränkungen finden Sie unter Einschränkungen (F#).
Der type-definition in der Syntax entspricht der Typdefinition eines nicht generischen Typs. Sie enthält die Konstruktorparameter für einen Klassentyp, eine optionale as-Klausel, das Gleichheitszeichen, die Datensatzfelder, die inherit-Klausel, die Optionen für eine Unterscheidungs-Union, let und do-Bindungen, Memberdefinitionen und sonstige in einer nicht generischen Typdefinition erlaubten Elemente.
Die anderen Syntaxelemente entsprechen den Syntaxelementen für nicht generische Funktionen und Typen. object-identifier ist z. B. ein Bezeichner, der das enthaltende Objekt selbst darstellt.
Eigenschaften, Felder und Konstruktoren können nicht generischer als der einschließende Typ sein. Zudem können Werte in einem Modul nicht generisch sein.
Implizit generische Konstrukte
Wenn vom F#-Compiler die Typen im Code per Rückschluss abgeleitet werden, wird jede Funktion, die generisch sein kann, auch als generisch behandelt. Wenn Sie explizit einen Typ angeben, z. B. einen Parametertyp, wird die automatische Verallgemeinerung verhindert.
Im folgenden Codebeispiel ist das makeList-Element generisch, obwohl weder es noch seine Parameter explizit als generisch deklariert sind.
let makeList a b =
[a; b]
Für die Signatur der Funktion wird 'a -> 'a -> 'a list abgeleitet. Beachten Sie, dass in diesem Beispiel für a und b der gleiche Typ abgeleitet wird. Dies ergibt sich, da beide Elemente in der gleichen Liste enthalten sind und alle Elemente einer Liste den gleichen Typ aufweisen müssen.
Sie können eine Funktion auch auf folgende Weise auf generisch festlegen: Verwenden Sie in einer Typanmerkung die einfache Anführungszeichensyntax, um anzugeben, dass der Parametertyp ein generischer Typparameter ist. Im folgenden Code ist das function1-Element 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 machen, indem Sie ihre Typparameter explizit in spitzen Klammern (< >) deklarieren. Dies wird im folgenden Code veranschaulicht.
let function2<'T> x y =
printfn "%A, %A" x y
Verwenden von generischen Konstrukten
Wenn Sie generische Funktionen oder Methoden verwenden, müssen Sie die Typargumente möglicherweise nicht angeben. Vom Compiler werden die entsprechenden Typargumente mithilfe des Typrückschlusses abgeleitet. Sollten immer noch Mehrdeutigkeiten vorhanden sein, setzen Sie die Typargumente in spitze Klammern, und trennen Sie mehrere Typargumente mit Kommas.
Im folgenden Code wird die Verwendung der in den vorherigen Abschnitten definierten Funktionen veranschaulicht.
// 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
Tipp
Es bestehen zwei Möglichkeiten, um mit Namen auf einen generischen Typ zu verweisen. list<int> und int list sind z. B. zwei Möglichkeiten, auf den generischen Typ list zu verweisen, der ein einzelnes Typargument int aufweist. Die letztgenannte Form wird im Allgemeinen nur mit integrierten F#-Typen, z. B. list und option, verwendet. Sind mehrere Typargumente vorhanden, wird normalerweise die Syntax Dictionary<int, string> verwendet. Sie können aber auch die Syntax (int, string) Dictionary verwenden.
Platzhalter als Typargumente
Anstatt eines benannten Typarguments, können Sie das Zeichen für Unterstrich bzw. das Platzhaltersymbol (_) verwenden, um anzugeben, dass ein Typargument vom Compiler abgeleitet werden soll. 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
Für einen generischen Typ oder eine Funktionsdefinition können nur Konstrukte verwendet werden, die bekanntermaßen für den generischen Typparameter verfügbar sind. Dies ist für die Aktivierung der Überprüfung von Funktions- und Methodenaufrufen zur Kompilierzeit erforderlich. Wenn Sie Typparameter explizit deklarieren, können Sie für einen generischen Typparameter eine explizite Einschränkung übernehmen, um den Compiler zu informieren, dass bestimmte Methoden und Funktionen verfügbar sind. Wenn Sie jedoch dem F#-Compiler ermöglichen, die generische Parametertypen per Rückschluss abzuleiten, werden die entsprechenden Einschränkungen automatisch bestimmt. Weitere Informationen finden Sie unter Einschränkungen (F#).
Statisch aufgelöste Typparameter
In F#-Programmen können zwei Arten von Typparametern verwendet werden. Bei der ersten Art handelt es sich um generische Typparameter, wie sie in den vorherigen Abschnitten beschrieben wurden. Diese erste Art von Typparameter entspricht den generischen Typparametern, die in Sprachen wie z. B. Visual Basic oder C# verwendet werden. Die andere Art von Typparameter ist für F# spezifisch und wird als statisch aufgelöster Typparameter bezeichnet. Informationen zu diesen Konstrukten finden Sie unter Statisch aufgelöste Typparameter (F#).
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
Siehe auch
Referenz
Statisch aufgelöste Typparameter (F#)