Génériques (F#)
Les valeurs de fonctions, méthodes, propriétés et types d'agrégats F# comme les classes, les enregistrements et les unions discriminées peuvent être génériques.Les constructions génériques contiennent au moins un paramètre de type généralement fourni par l'utilisateur de la construction générique.Les types et les fonctions génériques vous permettent d'écrire le code qui fonctionne avec plusieurs types, sans que ce dernier ne soit répété pour chaque type.Dans F#, il est très simple d'affecter à votre code un caractère générique. En effet, celui-ci est souvent et implicitement déduit pour devenir générique par l'inférence de type et les mécanismes de généralisation automatique du compilateur.
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifer.method-name<type-parameters> parameter-list [ return-type ] =
method-body
// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition
Notes
La déclaration d'un type ou d'une fonction explicitement générique s'apparente considérablement à celle d'un type ou d'une fonction non générique, sauf pour la spécification (et l'utilisation) des paramètres de type (entre crochets pointus après le nom du type ou de la fonction).
Les déclarations sont souvent implicitement génériques.Si vous ne spécifiez pas complètement le type de tous les paramètres utilisés pour composer un type ou une fonction, le compilateur tente de déduire le type de chaque paramètre, valeur et variable du code que vous écrivez.Pour plus d’informations, consultez Inférence de type (F#).Si le code de votre type ou fonction n'exerce pas de contrainte sur les types de paramètres, le type ou la fonction est implicitement générique.L'on appelle ce processus généralisation automatique.La généralisation automatique contient des limites.Par exemple, si le compilateur F# ne parvient pas à déduire les types d'une construction générique, le compilateur signale une erreur qui fait référence à une restriction que l'on appelle restriction de valeur.En pareil cas, il est possible que vous deviez ajouter des annotations de type.Pour plus d'informations sur la généralisation automatique et sur la restriction de valeur, ainsi que sur la modification de votre code pour traiter le problème, consultez Généralisation automatique (F#).
Dans la syntaxe précédente, type-parameters est une liste de paramètres avec la virgule comme séparateur qui représentent des types inconnus, dont chacun commence par un guillemet simple, éventuellement avec une clause de contrainte qui limite encore davantage les types susceptibles d'être utilisés pour ce type de paramètre.Pour en savoir plus sur la syntaxe concernant les clauses de contraintes de plusieurs genres et obtenir d'autres informations sur les contraintes, consultez Contraintes (F#).
type-definition dans la syntaxe est identique à la définition de type pour un type non générique.Cela inclut les paramètres de constructeur pour un type de classe, une clause as facultative, le symbole égal, les champs d'enregistrement, la clause inherit, les sélections pour une union discriminée, les liaisons let et do, les définitions de membres et tout autre élément autorisé dans une définition de type non générique.
Les autres éléments de syntaxe sont identiques à ceux de types et de fonctions non génériques.Par exemple, object-identifier est un identificateur qui représente l'objet conteneur lui-même.
Les propriétés, les champs et les constructeurs ne peuvent pas être plus génériques que le type englobant.De plus, les valeurs d'un module ne peuvent pas être génériques.
Constructions implicitement génériques
Lorsque le compilateur F# déduit les types dans votre code, il traite automatiquement toute fonction susceptible d'être générique en tant que telle.Si vous spécifiez un type explicitement, comme un type de paramètre, cela empêche toute généralisation automatique.
Dans l'exemple de code suivant, makeList est générique, même si ni celui-ci, ni ses paramètres ne sont explicitement déclarés comme étant génériques.
let makeList a b =
[a; b]
La signature de la fonction est déduite pour être 'a -> 'a -> 'a list.Notez que, dans cet exemple, a et b sont déduits pour avoir le même type.Cela est dû au fait qu'ils sont regroupés dans une liste et que tous les éléments d'une même liste doivent être du même type.
Vous pouvez également affecter un caractère générique à une fonction en utilisant la syntaxe contenant des guillemets simples dans une annotation de type, afin d'indiquer qu'un type de paramètre est un paramètre de type générique.Dans le code suivant, function1 est générique car ses paramètres sont déclarés de cette manière, comme des paramètres de type.
let function1 (x: 'a) (y: 'a) =
printfn "%A %A" x y
Constructions explicitement génériques
Vous pouvez également rendre une fonction générique en déclarant explicitement ses paramètres de type entre crochets pointus (< >).Le code suivant illustre ce comportement :
let function2<'T> x y =
printfn "%A, %A" x y
Utilisation de constructions génériques
Lorsque vous utilisez des fonctions ou des méthodes génériques, il est possible que vous n'ayez pas à spécifier les arguments de type.Le compilateur utilise l'inférence de type pour déduire les arguments de type appropriés.Si une ambigüité persiste, vous pouvez fournir des arguments de type entre crochets pointus, en prenant soin de séparer les différents arguments de type à l'aide de virgules.
Le code suivant présente l'utilisation des fonctions définies aux sections précédentes.
// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
// The compiler reports a warning:
function1<int> a b
// No warning.
function2<int> a b
[!REMARQUE]
Il existe deux manières de faire référence à un type générique sur la base du nom.Par exemple, list<int> et int list sont deux manières de faire référence à un type générique list qui dispose d'un argument de type unique int.Cette dernière forme n'est traditionnellement utilisée qu'avec des types F# intégrés, comme list et option.S'il existe plusieurs arguments de type, vous utilisez en principe la syntaxe Dictionary<int, string>, mais pouvez également utiliser la syntaxe (int, string) Dictionary.
Caractères génériques utilisés comme arguments de type
Pour spécifier qu'un argument de type doit être déduit par le compilateur, vous pouvez utiliser le trait de soulignement ou le caractère générique (_) à la place d'un argument de type nommé.Cela est illustré dans le code suivant.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
Contraintes concernant les types et fonctions génériques
Dans une définition de type ou de fonction générique, vous ne pouvez utiliser que les constructions connues pour être disponibles sur le paramètre de type générique.Cette exigence est imposée pour activer la vérification des appels de fonction et de méthode lors de la compilation.Si vous déclarez vos paramètres de type explicitement, vous pouvez appliquer une contrainte explicite à un paramètre de type générique pour notifier le compilateur que certaines méthodes et fonctions sont disponibles.Toutefois, si vous autorisez le compilateur F# à déduire vos types de paramètre génériques, il déterminera les contraintes appropriées pour vous.Pour plus d’informations, consultez Contraintes (F#).
Paramètres de type résolus statiquement
Il existe deux genres de paramètres de type qui peuvent être utilisés dans des programmes F#.Les premiers correspondent aux paramètres de type générique du genre décrit dans les précédentes sections.Ce premier genre de paramètre de type équivaut aux paramètres de type générique utilisés dans des langages tels que Visual Basic et C#.L'autre genre de paramètre de type est spécifique à F# et est appelé type de paramètre résolu statiquement.Pour plus d'informations sur ces constructions, consultez Paramètres de type résolus statiquement (F#).
Exemples
// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x : 'a) (y : 'a) =
printf "%A %A" x y
// A generic record, with the type parameter in angle brackets.
type GR<'a> =
{
Field1: 'a;
Field2: 'a;
}
// A generic class.
type C<'a>(a : 'a, b : 'a) =
let z = a
let y = b
member this.GenericMethod(x : 'a) =
printfn "%A %A %A" x y z
// A generic discriminated union.
type U<'a> =
| Choice1 of 'a
| Choice2 of 'a * 'a
type Test() =
// A generic member
member this.Function1<'a>(x, y) =
printfn "%A, %A" x y
// A generic abstract method.
abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
override this.abstractMethod<'a, 'b>(x:'a, y:'b) =
printfn "%A, %A" x y
Voir aussi
Référence
Paramètres de type résolus statiquement (F#)