Partager via


Génériques

Les valeurs de fonction F#, les méthodes, les propriétés et les types d’agrégation tels que 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 fonctions et types génériques vous permettent d’écrire du code qui fonctionne avec un large éventail de types sans répéter le code pour chaque type. Rendre votre code générique peut être simple en F#, car souvent votre code est implicitement déduit comme générique par l’inférence de type du compilateur et les mécanismes de généralisation automatique.

Syntaxe

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body

// Explicitly generic method.
[ static ] member object-identifier.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

Remarques

La déclaration d’une fonction ou d’un type explicitement générique est similaire à celle d’une fonction ou d’un type non générique, à l’exception de la spécification (et de l’utilisation) des paramètres de type, entre crochets angle après la fonction ou le nom de type.

Les déclarations sont souvent génériques implicitement. Si vous ne spécifiez pas entièrement le type de chaque paramètre utilisé pour composer une fonction ou un type, le compilateur tente de déduire le type de chaque paramètre, valeur et variable à partir du code que vous écrivez. Pour plus d’informations, consultez Type Inference. Si le code de votre type ou fonction ne limite pas les types de paramètres, la fonction ou le type est implicitement générique. Ce processus est nommé généralisation automatique. Il existe des limites sur la généralisation automatique. 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 appelée restriction de valeur. Dans ce cas, vous devrez peut-être ajouter des annotations de type. Pour plus d’informations sur la généralisation automatique et la restriction de valeur et sur la façon de modifier votre code pour résoudre le problème, consultez Généralisation automatique.

Dans la syntaxe précédente, les paramètres de type sont une liste séparée par des virgules de paramètres qui représentent des types inconnus, chacun commençant par un guillemet unique, éventuellement avec une clause de contrainte qui limite davantage les types pouvant être utilisés pour ce paramètre de type. Pour connaître la syntaxe des clauses de contrainte de différents types et d’autres informations sur les contraintes, consultez Contraintes.

La définition de type dans la syntaxe est la même que la définition de type pour un type non générique. Il inclut les paramètres de constructeur d’un type de classe, une clause facultative as , le symbole égal, les champs d’enregistrement, la inherit clause, les choix d’une union discriminatoire, let et do les liaisons, 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 des fonctions et types non génériques. Par exemple, l’identificateur d’objet 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. En outre, 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 toutes les fonctions qui peuvent être génériques comme génériques. Si vous spécifiez explicitement un type, tel qu’un type de paramètre, vous empêchez la généralisation automatique.

Dans l’exemple de code suivant, makeList est générique, même si ni ses paramètres ne sont explicitement déclarés comme génériques.

let makeList a b = [ a; b ]

La signature de la fonction est déduite comme 'a -> 'a -> 'a listétant . Notez que a dans b cet exemple, il est déduit d’avoir le même type. Cela est dû au fait qu’ils sont inclus dans une liste ensemble, et tous les éléments d’une liste doivent être du même type.

Vous pouvez également rendre une fonction générique à l’aide de la syntaxe de guillemet unique dans une annotation de type pour 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, en tant que paramètres de type.

let function1 (x: 'a) (y: 'a) = printfn "%A %A" x y

Constructions génériques explicitement

Vous pouvez également rendre une fonction générique en déclarant explicitement ses paramètres de type entre crochets (<type-parameter>). Le code suivant illustre cela.

let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y

Utilisation de constructions génériques

Lorsque vous utilisez des fonctions ou des méthodes génériques, vous n’avez peut-être pas besoin de spécifier les arguments de type. Le compilateur utilise l’inférence de type pour déduire les arguments de type appropriés. S’il existe toujours une ambiguïté, vous pouvez fournir des arguments de type entre crochets, en séparant plusieurs arguments de type avec des virgules.

Le code suivant montre l’utilisation des fonctions définies dans les 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 façons de faire référence à un type générique par nom. Par exemple, list<int> il int list existe deux façons de faire référence à un type list générique qui a un seul argument intde type. Ce dernier formulaire est traditionnellement utilisé uniquement avec des types F# intégrés tels que list et option. S’il existe plusieurs arguments de type, vous utilisez normalement la syntaxe Dictionary<int, string> , mais vous pouvez également utiliser la syntaxe (int, string) Dictionary.

Caractères génériques en tant qu’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 symbole générique (_), au lieu d’un argument de type nommé. Ceci est illustré dans le code suivant.

let printSequence (sequence1: Collections.seq<_>) =
    Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1

Contraintes dans les types et fonctions génériques

Dans une définition de type ou de fonction générique, vous pouvez utiliser uniquement les constructions connues pour être disponibles sur le paramètre de type générique. Cela est nécessaire pour activer la vérification des appels de fonction et de méthode au moment de la compilation. Si vous déclarez explicitement vos paramètres de type, vous pouvez appliquer une contrainte explicite à un paramètre de type générique pour informer le compilateur que certaines méthodes et fonctions sont disponibles. Toutefois, si vous autorisez le compilateur F# à déduire vos types de paramètres génériques, il détermine les contraintes appropriées pour vous. Pour plus d’informations, consultez Contraintes.

Paramètres de type résolus statiquement

Il existe deux types de paramètres de type qui peuvent être utilisés dans les programmes F#. Les premiers sont des paramètres de type générique du type décrit dans les sections précédentes. Ce premier type de paramètre de type est équivalent aux paramètres de type générique utilisés dans des langages tels que Visual Basic et C#. Un autre type de paramètre de type est spécifique à F# et est appelé paramètre de type résolu statiquement. Pour plus d’informations sur ces constructions, consultez Paramètres de type résolus statiquement.

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