Contraintes

Ce sujet décrit les contraintes que vous pouvez appliquer aux paramètres de type génériques pour spécifier les exigences d’un argument de type dans un type générique ou une fonction.

Syntaxe

type-parameter-list when constraint1 [ and constraint2]

Notes

Il existe plusieurs contraintes différentes que vous pouvez appliquer pour limiter les types qui peuvent être utilisés dans un type générique. Le tableau ci-dessous répertorie et présente ces contraintes.

Contrainte Syntaxe Description
Contrainte de type type-parameter :>type Le type fourni doit être égal ou dérivé du type spécifié, ou, si le type est une interface, le type fourni doit implémenter l’interface.
Contrainte Null type-parameter : null Le type fourni doit prendre en charge le littéral null. Cela inclut tous les types d’objets .NET, mais pas les types de liste F#, de tuple, de fonction, de classe, d’enregistrement ou d’union.
Contrainte membre explicite [(]type-parameter [ou ... ou type-parameter)] : (signature membre) Au moins l’un des arguments de type fournis doit avoir un membre qui a la signature spécifiée ; non destiné à une utilisation courante. Les membres doivent être définis explicitement sur le type ou une partie d’une extension de type implicite pour être des cibles valides pour une contrainte de membre explicite.
Contrainte de constructeur type-parameter : ( new : unit -> 'a ) La type fournit doit avoir un constructeur sans paramètre.
Contrainte de type valeur type-parameter : struct Le type fourni doit être un type de valeur .NET.
Contrainte de type de référence type-parameter : non struct Le type fourni doit être un type de référence .NET.
Contrainte de type d’énumération type-parameter : enum<underlying-type> Le type fourni doit être un type énuméré qui a le type sous-jacent spécifié ; non destiné à une utilisation courante.
Contrainte de délégué type-parameter : délégué<tuple-parameter-type, return-type> Le type fourni doit être un type délégué qui a les arguments et la valeur de retour spécifiés ; non destiné à une utilisation courante.
Contrainte de comparaison type-parameter : comparaison Le type fourni doit prendre en charge la comparaison.
Contrainte d’égalité type-parameter : égalité Le type fourni doit prendre en charge l’égalité.
Contrainte non managée type-parameter : non managé Le type fournit doit être un type non-géré. Les types non managés sont soit certains types primitifs (sbyte, byte, char, nativeint, unativeintfloat32floatint16uint16int32, uint32, , , int64, ou uint64decimal), des types d’énumération , nativeptr<_>soit une structure non générique dont les champs sont tous des types non managés.

Vous devez ajouter une contrainte lorsque votre code doit utiliser une fonctionnalité disponible sur le type de contrainte, mais pas sur les types en général. Par exemple, si vous utilisez la contrainte de type pour spécifier un type de classe, vous pouvez utiliser l’une des méthodes de cette classe dans la fonction ou le type générique.

La spécification de contraintes est parfois nécessaire lors de l’écriture explicite de paramètres de type, car sans contrainte, le compilateur n’a aucun moyen de vérifier que les fonctionnalités que vous utilisez seront disponibles sur n’importe quel type qui peut être fourni au moment de l’exécution pour le paramètre de type.

Les contraintes les plus courantes que vous utilisez dans le code F# sont les contraintes de type qui spécifient des classes de base ou des interfaces. Les autres contraintes sont soit utilisées par la bibliothèque F# pour implémenter certaines fonctionnalités, telles que la contrainte membre explicite, qui est utilisée pour implémenter la surcharge d’opérateur pour les opérateurs arithmétiques, soit sont fournies principalement parce que F# prend en charge l’ensemble complet de contraintes pris en charge par le Common Language Runtime.

Pendant le processus d’inférence de type, certaines contraintes sont déduites automatiquement par le compilateur. Par exemple, si vous utilisez l’opérateur + dans une fonction, le compilateur déduit une contrainte membre explicite sur les types de variables utilisés dans l’expression.

Le code suivant illustre certaines déclarations de contraintes :

// Base Type Constraint
type Class1<'T when 'T :> System.Exception> =
    class end

// Interface Type Constraint
type Class2<'T when 'T :> System.IComparable> =
    class end

// Null constraint
type Class3<'T when 'T : null> =
    class end

// Member constraint with instance member
type Class5<'T when 'T : (member Method1 : 'T -> int)> =
    class end

// Member constraint with property
type Class6<'T when 'T : (member Property1 : int)> =
    class end

// Constructor constraint
type Class7<'T when 'T : (new : unit -> 'T)>() =
    member val Field = new 'T()

// Reference type constraint
type Class8<'T when 'T : not struct> =
    class end

// Enumeration constraint with underlying value specified
type Class9<'T when 'T : enum<uint32>> =
    class end

// 'T must implement IComparable, or be an array type with comparable
// elements, or be System.IntPtr or System.UIntPtr. Also, 'T must not have
// the NoComparison attribute.
type Class10<'T when 'T : comparison> =
    class end

// 'T must support equality. This is true for any type that does not
// have the NoEquality attribute.
type Class11<'T when 'T : equality> =
    class end

type Class12<'T when 'T : delegate<obj * System.EventArgs, unit>> =
    class end

type Class13<'T when 'T : unmanaged> =
    class end

// Member constraints with two type parameters
// Most often used with static type parameters in inline functions
let inline add(value1 : ^T when ^T : (static member (+) : ^T * ^T -> ^T), value2: ^T) =
    value1 + value2

// ^T and ^U must support operator +
let inline heterogenousAdd(value1 : ^T when (^T or ^U) : (static member (+) : ^T * ^U -> ^T), value2 : ^U) =
    value1 + value2

// If there are multiple constraints, use the and keyword to separate them.
type Class14<'T,'U when 'T : equality and 'U : equality> =
    class end

Voir aussi