Delegados (F#)

Um representante representa uma chamada de função como um objeto. Na linguagem F#, é normalmente necessário usar valores de função para representar funções como valores de primeira classe. No entanto, os representantes são usados no .NET Framework e, portanto, são necessários ao interoperar com APIs que os esperam. Eles também podem ser usados ao criar bibliotecas projetadas para uso em outras linguagens do .NET Framework.

Sintaxe

type delegate-typename = delegate of type1 -> type2

Comentários

Na sintaxe anterior, type1 representa o(s) tipo(s) de argumento e type2 representa o tipo de retorno. Os tipos de argumento representados por type1 têm aplicação automática de currying. Isso sugere que, para esse tipo, você use uma forma de tupla se os argumentos da função de destino tiverem aplicação de currying e uma tupla entre parênteses para argumentos que já estão na forma de tupla. O currying automático remove um conjunto de parênteses, deixando um argumento de tupla que corresponde ao método de destino. Confira o exemplo de código para a sintaxe que deve ser usada em cada caso.

Os representantes podem ser anexados a valores de função de F# e métodos estáticos ou de instância. Os valores de função de F# podem ser transmitidos diretamente como argumentos para delegar construtores. Para um método estático, crie o representante usando o nome da classe e do método. Para um método de instância, forneça a instância e o método do objeto em um argumento. Em ambos os casos, o operador de acesso de membro (.) é usado.

O método Invoke no tipo de representante chama a função encapsulada. Além disso, os representantes podem ser transmitidos como valores de função fazendo referência ao nome do método Invoke sem os parênteses.

O código a seguir mostra a sintaxe para criar representantes que representam diversos métodos em uma classe. A sintaxe para declarar e atribuir o representante será um pouco diferente quando o método for estático ou de instância e quando ele tiver argumentos na forma de tupla ou de currying.

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)

O código a seguir mostra algumas das diferentes maneiras de trabalhar com representantes.

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

Veja a seguir a saída do exemplo de código anterior.

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

Os nomes podem ser adicionados em parâmetros delegados da seguinte forma:

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

Os nomes dos parâmetros delegados são opcionais e serão mostrados no método Invoke. Não é exigido que eles correspondam aos nomes dos parâmetros na implementação. Eles são permitidos apenas para a forma curried, mas não para a forma 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")

Veja a seguir a saída do exemplo de código anterior.

aa

Confira também