Compartir vía


Delegados (F#)

Un delegado representa una llamada de función como un objeto . En F#, normalmente debe usar valores de función para representar funciones como valores de primera clase; sin embargo, los delegados se usan en .NET Framework, por lo que son necesarios cuando se interoperan con las API que esperan. También se pueden usar al crear bibliotecas diseñadas para su uso desde otros lenguajes de .NET Framework.

Sintaxis

type delegate-typename = delegate of type1 -> type2

Observaciones

En la sintaxis anterior, type1 representa el tipo de argumento o los tipos y type2 representa el tipo de valor devuelto. Los tipos de argumento representados por type1 se consultan automáticamente. Esto sugiere que para este tipo se usa un formulario de tupla si los argumentos de la función de destino se consultan y una tupla entre paréntesis para los argumentos que ya están en el formulario de tupla. El currying automático quita un conjunto de paréntesis, dejando un argumento de tupla que coincide con el método de destino. Consulte el ejemplo de código para obtener la sintaxis que debe usar en cada caso.

Los delegados se pueden adjuntar a los valores de función de F# y a los métodos estáticos o de instancia. Los valores de función de F# se pueden pasar directamente como argumentos a constructores delegados. Para un método estático, se crea el delegado mediante el nombre de la clase y el método . Para un método de instancia, proporcione la instancia de objeto y el método en un argumento. En ambos casos, se usa el operador de acceso a miembros (.).

El Invoke método del tipo delegado llama a la función encapsulada. Además, los delegados se pueden pasar como valores de función haciendo referencia al nombre del método Invoke sin paréntesis.

En el código siguiente se muestra la sintaxis para crear delegados que representan varios métodos en una clase. Dependiendo de si el método es un método estático o un método de instancia, y si tiene argumentos en el formulario de tupla o en el formulario currido, la sintaxis para declarar y asignar el delegado es un poco diferente.

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)

En el código siguiente se muestran algunas de las distintas formas de trabajar con delegados.

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 salida del ejemplo de código anterior es la siguiente.

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

Los nombres se pueden agregar a parámetros delegados como:

// 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

Los nombres de parámetro delegado son opcionales y se mostrarán en el Invoke método . No son necesarios para que coincidan con los nombres de parámetro de la implementación. Solo se permiten para el formulario currido, pero no para el formulario 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 salida del ejemplo de código anterior es la siguiente.

aa

Consulte también