Flexible Typen
Eine flexible Typanmerkung gibt an, dass ein Parameter, eine Variable oder ein Wert einen Typ aufweist, der mit einem angegebenen Typ kompatibel ist. Die Kompatibilität wird anhand der Position in einer objektorientierten Hierarchie von Klassen oder Schnittstellen bestimmt. Flexible Typen sind vor allem dann nützlich, wenn die automatische Konvertierung in Typen, die in der Typenhierarchie höher stehen, nicht erfolgt, Sie aber dennoch möchten, dass Ihre Funktionalität mit jedem Typ in der Hierarchie oder jedem Typ, der eine Schnittstelle implementiert, möglich ist.
Syntax
#type
Bemerkungen
In der vorherigen Syntax stellt type einen Basistyp oder eine Schnittstelle dar.
Ein flexibler Typ entspricht einem generischen Typ mit einer Einschränkung, die die zulässigen Typen auf Typen begrenzt, die mit dem Basis- oder Schnittstellentyp kompatibel sind. Die folgenden beiden Codezeilen sind daher gleichwertig.
#SomeType
'T when 'T :> SomeType
Flexible Typen sind in verschiedenen Situationen nützlich. Wenn Sie beispielsweise eine Funktion höherer Ordnung haben (eine Funktion, die eine Funktion als Argument verwendet), ist es oft nützlich, dass die Funktion einen flexiblen Typ zurückgibt. Im folgenden Beispiel ermöglicht die Verwendung eines flexiblen Typs mit einem Sequenzargument in iterate2
der Funktion höherer Ordnung, mit Funktionen zu arbeiten, die Sequenzen, Arrays, Listen und andere aufzählbare Typen generieren.
Betrachten Sie die beiden folgenden Funktionen, von denen die eine eine Sequenz und die andere einen flexiblen Typ zurückgibt.
let iterate1 (f : unit -> seq<int>) =
for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
for e in f() do printfn "%d" e
// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)
// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])
Betrachten Sie als weiteres Beispiel die Bibliotheksfunktion Seq.concat:
val concat: sequences:seq<#seq<'T>> -> seq<'T>
Sie können eine der folgenden aufzählbaren Sequenzen an diese Funktion übergeben:
- Eine Liste mit Listen
- Eine Liste mit Arrays
- Ein Array mit Listen
- Ein Array mit Sequenzen
- Jede andere Kombination aufzählbarer Sequenzen
Im folgenden Code werden anhand von Seq.concat
die Szenarien veranschaulicht, die Sie mithilfe flexibler Typen unterstützen können.
let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]
let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1
let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]
let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2
let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3
let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4
let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }
let concat5 = Seq.concat [| seq1; seq2; seq3 |]
printfn "%A" concat5
Die Ausgabe lautet wie folgt.
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
In F# gibt es, wie in anderen objektorientierten Sprachen auch, Kontexte, in denen abgeleitete Typen oder Typen, die Schnittstellen implementieren, automatisch in einen Basis- oder Schnittstellentyp konvertiert werden. Diese automatischen Konvertierungen erfolgen in direkten Argumenten, aber nicht, wenn der Typ an einer untergeordneten Position, als Teil eines komplexeren Typs, wie z. B. eines Rückgabetyps eines Funktionstyps, oder als Typargument vorliegt. Daher ist die flexible Typnotation in erster Linie nützlich, wenn der Typ, auf den Sie sie anwenden, Teil eines komplexeren Typs ist.