Délégués (F#)

Un délégué représente un appel de fonction en tant qu’objet. En F#, vous devez généralement utiliser des valeurs de fonction pour représenter des fonctions en tant que valeurs de première classe ; Toutefois, les délégués sont utilisés dans .NET Framework et sont donc nécessaires lorsque vous interagissez avec les API qui les attendent. Vous pouvez également les utiliser lors de la création de bibliothèques conçues pour une utilisation à partir d’autres langages .NET Framework.

Syntaxe

type delegate-typename = delegate of type1 -> type2

Notes

Dans la syntaxe précédente, type1 représente le ou les types d’arguments, et type2 représente le type de retour. Les types d’arguments représentés par type1 sont automatiquement curryfiés. Cela suggère que pour ce type, vous utilisez une forme de tuple si les arguments de la fonction cible sont bouclés, et un tuple entre parenthèses pour les arguments qui se trouvent déjà dans la forme du tuple. La curryfication automatique supprime un ensemble de parenthèses, en laissant un argument de tuple qui correspond à la méthode cible. Reportez-vous à l’exemple de code pour connaître la syntaxe que vous devez utiliser dans chaque cas.

Des délégués peuvent être attachés à des valeurs de fonction F#, ainsi qu’à des méthodes statiques ou d’instance. Les valeurs de fonction F# peuvent être passées directement en tant qu’arguments aux constructeurs délégués. Pour une méthode statique, vous construisez le délégué à l’aide du nom de la classe et de la méthode. Pour une méthode d’instance, vous fournissez l’objet d’instance et la méthode dans un seul argument. Dans les deux cas, l’opérateur d’accès aux membres (.) est utilisé.

La méthode Invoke sur le type délégué appelle la fonction encapsulée. En outre, les délégués peuvent être passés en tant que valeurs de fonction en référençant le nom de la méthode Invoke sans parenthèses.

Le code suivant montre la syntaxe de création de délégués qui représentent différentes méthodes dans une classe. Selon que la méthode est une méthode statique ou une méthode d’instance et qu’elle a des arguments dans la forme de tuple ou curryfiée, la syntaxe de déclaration et d’affectation du délégué est légèrement différente.

type Test1() =
  static member add(a : int, b : int) =
     a + b
  static member add2 (a : int) (b : int) =
     a + b

  member x.Add(a : int, b : int) =
     a + b
  member x.Add2 (a : int) (b : int) =
     a + b


// Delegate1 works with tuple arguments.
type Delegate1 = delegate of (int * int) -> int
// Delegate2 works with curried arguments.
type Delegate2 = delegate of int * int -> int

let InvokeDelegate1 (dlg: Delegate1) (a: int) (b: int) =
   dlg.Invoke(a, b)
let InvokeDelegate2 (dlg: Delegate2) (a: int) (b: int) =
   dlg.Invoke(a, b)

// For static methods, use the class name, the dot operator, and the
// name of the static method.
let del1 = Delegate1(Test1.add)
let del2 = Delegate2(Test1.add2)

let testObject = Test1()

// For instance methods, use the instance value name, the dot operator, and the instance method name.
let del3 = Delegate1(testObject.Add)
let del4 = Delegate2(testObject.Add2)

for (a, b) in [ (100, 200); (10, 20) ] do
  printfn "%d + %d = %d" a b (InvokeDelegate1 del1 a b)
  printfn "%d + %d = %d" a b (InvokeDelegate2 del2 a b)
  printfn "%d + %d = %d" a b (InvokeDelegate1 del3 a b)
  printfn "%d + %d = %d" a b (InvokeDelegate2 del4 a b)

Le code suivant montre certaines des différentes façons dont vous pouvez utiliser les délégués.

type Delegate1 = delegate of int * char -> string

let replicate n c = String.replicate n (string c)

// An F# function value constructed from an unapplied let-bound function
let function1 = replicate

// A delegate object constructed from an F# function value
let delObject = Delegate1(function1)

// An F# function value constructed from an unapplied .NET member
let functionValue = delObject.Invoke

List.map (fun c -> functionValue(5,c)) ['a'; 'b'; 'c']
|> List.iter (printfn "%s")

// Or if you want to get back the same curried signature
let replicate' n c =  delObject.Invoke(n,c)

// You can pass a lambda expression as an argument to a function expecting a compatible delegate type
// System.Array.ConvertAll takes an array and a converter delegate that transforms an element from
// one type to another according to a specified function.
let stringArray = System.Array.ConvertAll([|'a';'b'|], fun c -> replicate' 3 c)
printfn "%A" stringArray

La sortie de l’exemple de code précédent est la suivante.

aaaaa
bbbbb
ccccc
[|"aaa"; "bbb"|]

Les noms peuvent être ajoutés aux paramètres délégués comme suit :

// http://www.pinvoke.net/default.aspx/user32/WinEventDelegate.html
type WinEventDelegate = delegate of hWinEventHook:nativeint * eventType:uint32 * hWnd:nativeint * idObject:int * idChild:int * dwEventThread:uint32 * dwmsEventTime:uint32 -> unit

Les noms de paramètres délégués sont facultatifs et sont affichés dans la méthode Invoke. Ils ne sont pas nécessaires pour faire correspondre les noms de paramètres dans l’implémentation. Ils ne sont autorisés que pour la forme curry, mais pas pour la forme tupled.

type D1 = delegate of item1: int * item2: string -> unit
let a = D1(fun a b -> printf "%s" b)
a.Invoke(item2 = "a", item1 = 1) // Calling with named arguments

type D2 = delegate of int * item2: string -> unit // Omitting one name
let b = D2(fun a b -> printf "%s" b)
b.Invoke(1, item2 = "a")

La sortie de l’exemple de code précédent est la suivante.

aa

Voir aussi