Types flexibles

Une annotation de type flexible indique qu'un paramètre, une variable ou une valeur a un type compatible avec un type spécifié, où la compatibilité est déterminée par la position dans une hiérarchie orientée objet de classes ou d'interfaces. Les types flexibles sont particulièrement utiles lorsque la conversion automatique vers des types plus élevés dans la hiérarchie des types n'a pas lieu, mais que vous souhaitez que votre fonctionnalité fonctionne avec n'importe quel type de la hiérarchie ou n'importe quel type qui met en œuvre une interface.

Syntaxe

#type

Notes

Dans la syntaxe précédente, type représente un type de base ou une interface.

Un type flexible est équivalent à un type générique doté d'une contrainte limitant les types autorisés aux types compatibles avec le type de base ou d'interface. Autrement dit, les deux lignes de code suivantes sont équivalentes.

#SomeType

'T when 'T :> SomeType

Les types flexibles sont utiles dans plusieurs situations. Par exemple, lorsque vous avez une fonction d'ordre supérieur (une fonction qui prend une fonction comme argument), il est souvent utile que la fonction renvoie un type flexible. Dans l’exemple suivant, l’utilisation d’un type flexible avec un argument de séquence iterate2 permet à la fonction d’ordre supérieur d’utiliser des fonctions qui génèrent des séquences, des tableaux, des listes et tout autre type énumérable.

Considérez les deux fonctions suivantes, dont l’une retourne une séquence, et l’autre retourne un type flexible.

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

Comme autre exemple, considérez la fonction de bibliothèque Seq.concat :

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

Vous pouvez passer l’une des séquences énumérables suivantes à cette fonction :

  • Une liste de listes
  • Une liste de tableaux
  • Un tableau de listes
  • Un tableau de séquences
  • Toute autre combinaison de séquences énumérables

Le code suivant utilise Seq.concat pour illustrer les scénarios que vous pouvez prendre en charge à l’aide de types flexibles.

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

La sortie est la suivante.

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

Dans F#, comme dans d’autres langages orientés objet, il existe des contextes dans lesquels les types ou types dérivés qui implémentent des interfaces sont automatiquement convertis en type de base ou type d’interface. Ces conversions automatiques se produisent dans les arguments directs, mais pas lorsque le type se trouve dans une position subordonnée, en tant que partie d'un type plus complexe tel que le type de retour d'un type de fonction, ou en tant qu'argument d'un type. Par conséquent, la notation de type flexible est principalement utile lorsque le type auquel vous l’appliquez fait partie d’un type plus complexe.

Voir aussi