Dela via


Flexibla typer

En flexibel typanteckning anger att en parameter, variabel eller ett värde har en typ som är kompatibel med en angiven typ, där kompatibilitet bestäms av position i en objektorienterad hierarki med klasser eller gränssnitt. Flexibla typer är särskilt användbara när den automatiska konverteringen till typer som är högre i typhierarkin inte sker, men du fortfarande vill att dina funktioner ska fungera med någon typ i hierarkin eller någon typ som implementerar ett gränssnitt.

Syntax

#type

Kommentarer

I föregående syntax representerar typen en bastyp eller ett gränssnitt.

En flexibel typ motsvarar en allmän typ som har en begränsning som begränsar de tillåtna typerna till typer som är kompatibla med bas- eller gränssnittstypen. Det vill: följande två kodrader är likvärdiga.

#SomeType

'T when 'T :> SomeType

Flexibla typer är användbara i flera typer av situationer. När du till exempel har en funktion med högre ordning (en funktion som tar en funktion som argument) är det ofta användbart att funktionen returnerar en flexibel typ. I följande exempel gör användningen av en flexibel typ med ett sekvensargument i iterate2 att funktionen med högre ordning kan fungera med funktioner som genererar sekvenser, matriser, listor och andra uppräkningsbara typer.

Tänk på följande två funktioner, varav en returnerar en sekvens, varav den andra returnerar en flexibel typ.

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])

Som ett annat exempel bör du överväga biblioteksfunktionen Seq.concat :

val concat: sequences:seq<#seq<'T>> -> seq<'T>

Du kan skicka någon av följande uppräkningsbara sekvenser till den här funktionen:

  • En lista med listor
  • En lista över matriser
  • En matris med listor
  • En matris med sekvenser
  • Alla andra kombinationer av uppräkningsbara sekvenser

Följande kod används Seq.concat för att demonstrera de scenarier som du kan stödja med hjälp av flexibla typer.

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

Utdata är följande.

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; ...]

I F#, precis som i andra objektorienterade språk, finns det kontexter där härledda typer eller typer som implementerar gränssnitt automatiskt konverteras till en bastyp eller gränssnittstyp. Dessa automatiska konverteringar sker i direkta argument, men inte när typen är i en underordnad position, som en del av en mer komplex typ, till exempel en returtyp av en funktionstyp eller som ett typargument. Därför är den flexibla typ notationen främst användbar när den typ som du tillämpar den på är en del av en mer komplex typ.

Se även