Partager via


Extensions de type (F#)

Les extensions de type vous permettent d'ajouter de nouveaux membres à un type d'objet précédemment défini.

// Intrinsic extension.
type typename with
   member self-identifier.member-name =
      body
   ...
   [ end ]

// Optional extension.
type typename with
   member self-identifier.member-name =
      body
   ...
   [ end ]

Notes

Il existe deux formes d'extensions de type. Celles-ci présentent une syntaxe et un comportement légèrement différents. Une extension intrinsèque est une extension qui figure dans le même espace de noms ou module, dans le même fichier source et dans le même assembly (DLL ou fichier exécutable) que le type qui est étendu. Une extension facultative est une extension qui figure à l'extérieur du module, de l'espace de noms ou de l'assembly d'origine du type qui est étendu. Les extensions intrinsèques apparaissent sur le type lorsque le type est examiné par réflexion, ce qui n'est pas le cas des extensions facultatives. Les extensions facultatives doivent se trouver dans des modules, et sont uniquement dans la portée lorsque le module qui contient l'extension est ouvert.

Dans la syntaxe précédente, typename représente le type qui est étendu. N'importe quel type accessible peut être étendu, mais le nom du type doit être un nom de type réel, et non une abréviation de type. Vous pouvez définir plusieurs membres dans une extension de type. self-identifier représente l'instance de l'objet qui est appelée, comme dans des membres ordinaires.

Le mot clé end est facultatif dans la syntaxe simplifiée.

Les membres définis dans des extensions de type peuvent être utilisés comme tout autre membre sur un type de classe. À l'instar d'autres membres, ils peuvent être statiques ou d'instance. Ces méthodes sont également appelées méthodes d'extension ; les propriétés sont appelées propriétés d'extension, etc. Les membres d'extension facultatifs sont compilés en membres statiques pour lesquels l'instance de l'objet est passée implicitement comme premier paramètre. Toutefois, ils se comportent comme s'ils étaient des membres d'instances ou des membres statiques selon la façon dont ils sont déclarés. Des membres d'extension implicites sont inclus comme membres du type et peuvent être utilisés sans restriction.

Les méthodes d'extension ne peuvent pas être des méthodes virtuelles ou abstraites. Elles peuvent surcharger d'autres méthodes du même nom, mais le compilateur donne la préférence aux méthodes de non-extension dans le cas d'un appel ambigu.

Si plusieurs extensions de type intrinsèques existent pour un type, tous les membres doivent être uniques. Pour les extensions de type facultatives, les membres dans différentes extensions de type du même type peuvent avoir les mêmes noms. Des erreurs d'ambiguïté se produisent uniquement si le code client ouvre deux portées différentes qui définissent les mêmes noms de membre.

Dans l'exemple suivant, un type dans un module a une extension de type intrinsèque. Pour le code client à l'extérieur du module, l'extension de type apparaît comme un membre standard du type à tous points de vue.

module MyModule1 =

    // Define a type. 
    type MyClass() =
      member this.F() = 100

    // Define type extension. 
    type MyClass with 
       member this.G() = 200

module MyModule2 =
   let function1 (obj1: MyModule1.MyClass) =
      // Call an ordinary method.
      printfn "%d" (obj1.F())
      // Call the extension method.
      printfn "%d" (obj1.G())

Vous pouvez utiliser des extensions de type intrinsèques pour séparer la définition d'un type en sections. Cela peut être utile pour la gestion de définitions de type importantes, par exemple pour conserver le code généré par le compilateur et le code créé séparés, ou pour grouper le code créé par des personnes différentes ou associé à différentes fonctionnalités.

Dans l'exemple suivant, une extension de type facultative étend le type System.Int32 avec une méthode d'extension FromString qui appelle le membre statique Parse. La méthode testFromString montre que le nouveau membre est appelé comme n'importe quel membre d'instance.

// Define a new member method FromString on the type Int32. 
type System.Int32 with 
    member this.FromString( s : string ) =
       System.Int32.Parse(s)

let testFromString str =  
    let mutable i = 0
    // Use the extension method.
    i <- i.FromString(str)
    printfn "%d" i

testFromString "500"

Le nouveau membre d'instance apparaît comme toute autre méthode du type Int32 dans IntelliSense, mais uniquement lorsque le module contenant l'extension est ouvert ou dans la portée.

Méthodes d'extension génériques

Avant F# 3,1, le compilateur F# ne prenait pas en charge l'utilisation des méthodes d'extension de style C# avec une variable de type générique, un type de tableau, un type de tuple ou un type de fonction F# comme paramètre « this ». F# 3.1 prend en charge l'utilisation de ces membres d'extension.

Par exemple, dans le code F# 3.1, vous pouvez utiliser des méthodes d'extension avec des signatures qui ressemblent à la syntaxe suivante en C# :

static member Method<T>(this T input, T other)

Cette approche est particulièrement utile lorsque le paramètre de type générique est contraint. De plus, vous pouvez désormais déclarer les membres d'extension comme suit dans le code F# et définir un ensemble sémantiquement riche de méthodes d'extension. En F#, vous définissez généralement des membres d'extension comme illustré dans l'exemple suivant :

type seq<’T> with
    /// Repeat each element of the sequence n times
    member xs.RepeatElements(n: int) =
        seq { for x in xs do for i in 1 .. n do yield x }

Toutefois, pour un type générique, il se peut que la variable de type ne soit pas contrainte. Vous pouvez désormais déclarer un membre d'extension de style C# dans F# pour contourner cette limitation. Lorsque vous combinez ce type de déclaration avec la fonctionnalité inline de F#, vous pouvez présenter les algorithmes génériques comme membres d'extension.

Prenons les déclarations suivantes :

[<Extension>]
type ExtraCSharpStyleExtensionMethodsInFSharp () =
    [<Extension>]
    static member inline Sum(xs: seq<’T>) = Seq.sum xs

En utilisant cette déclaration, vous pouvez écrire du code qui ressemble à l'exemple suivant.

let listOfIntegers = [ 1 .. 100 ]
let listOfBigIntegers = [ 1I to 100I ]
let sum1 = listOfIntegers.Sum()
let sum2 = listOfBigIntegers.Sum()

Dans ce code, le même code arithmétique générique est appliqué aux listes de deux types sans surcharge, en définissant un seul membre d'extension.

Voir aussi

Autres ressources

Référence du langage F#

Membres (F#)