Delegados (F#)
Un delegado representa una llamada de función como un objeto. En F#, lo habitual es utilizar valores de función para representar las funciones como valores de primera clase; sin embargo, los delegados se utilizan en .NET Framework y, por consiguiente, son necesarios para interoperar con las API que los esperan. También se pueden utilizar para crear bibliotecas que se van a usar en otros lenguajes .NET Framework.
type delegate-typename = delegate of type1 -> type2
Comentarios
En la sintaxis anterior, type1 representa el tipo o los tipos de argumento y type2 representa el tipo de valor devuelto. Los tipos de argumentos representados por type1 se currifican automáticamente. Esto sugiere que, para este tipo, se utilice la forma de tupla si están currificados los argumentos de la función de destino, y una tupla entre paréntesis para los argumentos que ya están en forma de tupla. La currificación automática quita un juego de paréntesis y deja un argumento de tupla que coincide con el método de destino. Consulte el ejemplo de código para obtener la sintaxis que se debe utilizar en cada caso.
Los delegados se pueden adjuntar a los valores de función de F#, así como a los métodos estáticos o de instancia. Los valores de función de F# se pueden pasar directamente como argumentos para constructores delegados. Para un método estático, el delegado se construye mediante el nombre de la clase y el método. Para un método de instancia, se proporcionan la instancia de objeto y el método en un argumento. En ambos casos se utiliza el operador de acceso a miembros (.).
El método Invoke del tipo de delegado llama a la función encapsulada. Asimismo, los delegados se pueden pasar como valores de función haciendo referencia al nombre del método Invoke sin los paréntesis.
En el código siguiente se muestra la sintaxis para crear delegados que representan varios métodos de una clase. La sintaxis para declarar y asignar el delegado varía ligeramente dependiendo de si el método es estático o de instancia, y de si tiene argumentos en forma de tupla o currificada.
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 = new Delegate1( Test1.add )
let del2 : Delegate2 = new 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 = new Delegate1( testObject.Add )
let del4 : Delegate2 = new 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)
El siguiente código muestra algunas de las distintas maneras 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 = new 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"|]