Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
Les extensions de type (également appelées augmentations) sont une famille de fonctionnalités qui vous permettent d’ajouter de nouveaux membres à un type d’objet précédemment défini. Les trois fonctionnalités sont les suivantes :
- Extensions de type intrinsèques
- Extensions de type facultatives
- Méthodes d’extension
Chacun peut être utilisé dans différents scénarios et a des compromis différents.
Syntaxe
// Intrinsic and optional extensions
type typename with
member self-identifier.member-name =
body
...
// Extension methods
open System.Runtime.CompilerServices
[<Extension>]
type Extensions() =
[<Extension>]
static member extension-name (ty: typename, [args]) =
body
...
Extensions de type intrinsèques
Une extension de type intrinsèque est une extension de type qui étend un type défini par l’utilisateur.
Les extensions de type intrinsèque doivent être définies dans le même fichier et dans le même espace de noms ou le même module que le type qu’ils étendent. Toute autre définition entraîne l’ajout d’extensions de type facultatives.
Les extensions de type intrinsèques sont parfois un moyen plus propre de séparer les fonctionnalités de la déclaration de type. L’exemple suivant montre comment définir une extension de type intrinsèque :
namespace Example
type Variant =
| Num of int
| Str of string
module Variant =
let print v =
match v with
| Num n -> printf "Num %d" n
| Str s -> printf "Str %s" s
// Add a member to Variant as an extension
type Variant with
member x.Print() = Variant.print x
L’utilisation d’une extension de type vous permet de séparer chacune des options suivantes :
- Déclaration d’un
Varianttype - Fonctionnalité permettant d’imprimer la
Variantclasse en fonction de sa « forme » - Un moyen d’accéder à la fonctionnalité d’impression avec la notation de style
.objet
Il s’agit d’une alternative à la définition de tout en tant que membre sur Variant. Bien qu’il ne s’agit pas d’une approche intrinsèquement meilleure, il peut s’agir d’une représentation plus claire des fonctionnalités dans certaines situations.
Les extensions de type intrinsèques sont compilées en tant que membres du type qu’elles augmentent et apparaissent sur le type lorsque le type est examiné par réflexion.
Extensions de type facultatives
Une extension de type facultative est une extension qui apparaît en dehors du module d’origine, de l’espace de noms ou de l’assembly du type en cours d’extension.
Les extensions de type facultatives sont utiles pour étendre un type que vous n’avez pas défini vous-même. Par exemple:
module Extensions
type IEnumerable<'T> with
/// Repeat each element of the sequence n times
member xs.RepeatElements(n: int) =
seq {
for x in xs do
for _ in 1 .. n -> x
}
Vous pouvez désormais accéder RepeatElements comme s’il s’agit d’un membre tant IEnumerable<T> que le Extensions module est ouvert dans l’étendue dans laquelle vous travaillez.
Les extensions facultatives n’apparaissent pas sur le type étendu lorsqu’elles sont examinées par réflexion. Les extensions facultatives doivent être dans les modules et elles sont uniquement dans l’étendue lorsque le module qui contient l’extension est ouvert ou est dans l’étendue.
Les membres d’extension facultatifs sont compilés en membres statiques pour lesquels l’instance d’objet est passée implicitement en tant que premier paramètre. Toutefois, ils agissent comme s’ils sont des membres d’instance ou des membres statiques en fonction de la façon dont ils sont déclarés.
Les membres d’extension facultatifs ne sont pas non plus visibles par les consommateurs C# ou Visual Basic. Ils ne peuvent être consommés que dans d’autres codes F#.
Limitation générique des extensions de type intrinsèques et facultatives
Il est possible de déclarer une extension de type sur un type générique où la variable de type est contrainte. L’exigence est que la contrainte de la déclaration d’extension correspond à la contrainte du type déclaré.
Toutefois, même si des contraintes sont mises en correspondance entre un type déclaré et une extension de type, il est possible qu’une contrainte soit déduite par le corps d’un membre étendu qui impose une exigence différente du paramètre de type que le type déclaré. Par exemple:
open System.Collections.Generic
// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
member this.Sum() = Seq.sum this
Il n’existe aucun moyen d’obtenir ce code pour qu’il fonctionne avec une extension de type facultative :
- Comme c’est le cas, le
Summembre a une contrainte différente sur'T(static member get_Zeroetstatic member (+)) de ce que définit l’extension de type. - La modification de l’extension de type pour avoir la même contrainte que
Sumcelle-ci ne correspond plus à la contrainte définie surIEnumerable<'T>. -
member inline this.SumLa modificationmember this.Sumdonne une erreur indiquant que les contraintes de type sont incompatibles.
Ce qui est souhaité est des méthodes statiques qui « flottent dans l’espace » et peuvent être présentées comme s’ils étendent un type. C’est là que les méthodes d’extension deviennent nécessaires.
Méthodes d’extension
Enfin, les méthodes d’extension (parfois appelées « membres d’extension de style C# ») peuvent être déclarées en F# comme méthode membre statique sur une classe.
Les méthodes d’extension sont utiles lorsque vous souhaitez définir des extensions sur un type générique qui limite la variable de type. Par exemple:
namespace Extensions
open System.Collections.Generic
open System.Runtime.CompilerServices
[<Extension>]
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
Lorsqu’il est utilisé, ce code apparaît comme s’il Sum est défini sur IEnumerable<T>, tant qu’il Extensions a été ouvert ou est dans l’étendue.
Pour que l’extension soit disponible pour VB.NET code, un supplément ExtensionAttribute est requis au niveau de l’assembly :
module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()
Autres remarques
Les extensions de type ont également les attributs suivants :
- Tout type accessible est étendu.
- Les extensions de type intrinsèques et facultatives peuvent définir n’importe quel type de membre, pas seulement les méthodes. Ainsi, les propriétés d’extension sont également possibles, par exemple.
- Le
self-identifierjeton de la syntaxe représente l’instance du type appelé, tout comme les membres ordinaires. - Les membres étendus peuvent être des membres statiques ou d’instance.
- Les variables de type sur une extension de type doivent correspondre aux contraintes du type déclaré.
Les limitations suivantes existent également pour les extensions de type :
- Les extensions de type ne prennent pas en charge les méthodes virtuelles ou abstraites.
- Les extensions de type ne prennent pas en charge les méthodes de remplacement comme augmentations.
- Les extensions de type ne prennent pas en charge les paramètres de type résolus statiquement.
- Les extensions de type facultatives ne prennent pas en charge les constructeurs comme des augmentations.
- Les extensions de type ne peuvent pas être définies sur les abréviations de type.
- Les extensions de type ne sont pas valides pour
byref<'T>(bien qu’elles puissent être déclarées). - Les extensions de type ne sont pas valides pour les attributs (bien qu’elles puissent être déclarées).
- Vous pouvez définir des extensions qui surchargent d’autres méthodes du même nom, mais le compilateur F# donne la préférence aux méthodes non-extension s’il existe un appel ambigu.
Enfin, si plusieurs extensions de type intrinsèque existent pour un seul type, tous les membres doivent être uniques. Pour les extensions de type facultatives, les membres de différentes extensions de type au même type peuvent avoir les mêmes noms. Les erreurs d’ambiguïté se produisent uniquement si le code client ouvre deux étendues différentes qui définissent les mêmes noms de membres.