Tipi flessibili (F#)

Un'annotazione di tipo flessibile indica che un parametro, una variabile o un valore è di un tipo compatibile con un tipo specificato, in cui la compatibilità viene determinata dalla posizione in una gerarchia orientata agli oggetti di classi o interfacce. I tipi flessibili sono utili in particolare quando non si verifica la conversione automatica ai tipi più in alto nella gerarchia ma si desidera comunque consentire l'utilizzo della funzionalità con qualsiasi tipo nella gerarchia o qualsiasi tipo che implementa un'interfaccia.

#type

Note

Nella sintassi precedente type rappresenta un'interfaccia o un tipo di base.

Un tipo flessibile è equivalente a un tipo generico con un vincolo che limita i tipi consentiti ai tipi compatibili con il tipo di interfaccia o di base. Le due righe di codice seguenti sono pertanto equivalenti.

#SomeType

'a when 'a :> SomeType

I tipi flessibili sono utili in diverse situazioni. Quando, ad esempio, si dispone di una funzione di ordine superiore (una funzione che accetta una funzione come argomento), è spesso utile fare in modo che la funzione restituisca un tipo flessibile. Nell'esempio seguente l'utilizzo di un tipo flessibile con un argomento di sequenza in iterate2 consente l'utilizzo della funzione di ordine superiore con funzioni che generano sequenze, matrici, elenchi e qualsiasi altro tipo enumerabile.

Si prendano in considerazione le due funzioni seguenti, delle quali una restituisce una sequenza e l'altra un tipo flessibile.

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

Come altro esempio, si consideri la funzione della libreria Seq.concat:

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

A questa funzione è possibile passare una qualsiasi delle sequenze enumerabili seguenti:

  • Un elenco di elenchi

  • Un elenco di matrici

  • Una matrice di elenchi

  • Una matrice di sequenze

  • Qualsiasi altra combinazione di sequenze enumerabili

Nel codice seguente viene utilizzato Seq.concat per illustrare gli scenari che è possibile supportare utilizzando tipi flessibili.

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

L'output è indicato di seguito.

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#, come negli altri linguaggi orientati a oggetti, vi sono contesti in cui i tipi o i tipi derivati che implementano interfacce vengono convertiti automaticamente in un tipo di base o in un tipo di interfaccia. Queste conversioni automatiche vengono eseguite negli argomenti diretti, ma non quando il tipo è in una posizione subordinata, come parte di un tipo più complesso, come tipo restituito di un tipo di funzione o come argomento di tipo. La notazione di tipo flessibile è pertanto utile principalmente quando il tipo al quale si applica fa parte di un tipo più complesso.

Vedere anche

Riferimenti

Generics (F#)

Altre risorse

Riferimenti per il linguaggio F#