Udostępnij za pośrednictwem


Typy elastyczne

Adnotacja typu elastycznego wskazuje, że parametr, zmienna lub wartość ma typ zgodny z określonym typem, gdzie zgodność jest określana przez położenie w hierarchii obiektowej klas lub interfejsów. Typy elastyczne są przydatne w szczególności wtedy, gdy automatyczna konwersja na typy wyższe w hierarchii typów nie występuje, ale nadal chcesz umożliwić pracę funkcji z dowolnym typem w hierarchii lub dowolnego typu, który implementuje interfejs.

Składnia

#type

Uwagi

W poprzedniej składni typ reprezentuje typ podstawowy lub interfejs.

Typ elastyczny jest odpowiednikiem typu ogólnego, który ma ograniczenie ograniczające dozwolone typy do typów, które są zgodne z typem podstawowym lub interfejsu. Oznacza to, że następujące dwa wiersze kodu są równoważne.

#SomeType

'T when 'T :> SomeType

Typy elastyczne są przydatne w kilku typach sytuacji. Na przykład jeśli masz funkcję wyższej kolejności (funkcja, która przyjmuje funkcję jako argument), często przydaje się, aby funkcja zwracała typ elastyczny. W poniższym przykładzie użycie typu elastycznego z argumentem sekwencji w programie iterate2 umożliwia pracę funkcji wyższej kolejności z funkcjami, które generują sekwencje, tablice, listy i dowolny inny typ wyliczalny.

Rozważmy następujące dwie funkcje, z których jedna zwraca sekwencję, z których druga zwraca typ elastyczny.

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

W innym przykładzie rozważmy funkcję biblioteki Seq.concat :

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

Do tej funkcji można przekazać dowolną z następujących sekwencji wyliczalnych:

  • Lista list
  • Lista tablic
  • Tablica list
  • Tablica sekwencji
  • Dowolna inna kombinacja sekwencji wyliczalnych

Poniższy kod używa Seq.concat metody do zademonstrowania scenariuszy, które można obsługiwać przy użyciu typów elastycznych.

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

Dane wyjściowe są następujące:

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

W języku F#, podobnie jak w innych językach obiektowych, istnieją konteksty, w których typy pochodne lub typy implementujące interfejsy są automatycznie konwertowane na typ podstawowy lub typ interfejsu. Te konwersje automatyczne występują w argumentach bezpośrednich, ale nie wtedy, gdy typ znajduje się w pozycji podrzędnej, w ramach bardziej złożonego typu, takiego jak zwracany typ funkcji lub jako argument typu. W związku z tym notacja typu elastycznego jest przydatna przede wszystkim wtedy, gdy typ, do którego jest stosowany, jest częścią bardziej złożonego typu.

Zobacz też