Delegati (F#)
Un delegato rappresenta una chiamata di funzione come oggetto . In F#, in genere è consigliabile usare valori di funzione per rappresentare le funzioni come valori di prima classe; Tuttavia, i delegati vengono usati in .NET Framework e quindi sono necessari quando si interagisce con le API che le prevedono. Possono essere usati anche per la creazione di librerie progettate per l'uso da altri linguaggi .NET Framework.
Sintassi
type delegate-typename = delegate of type1 -> type2
Osservazioni:
Nella sintassi precedente rappresenta type1
il tipo di argomento o i tipi e type2
rappresenta il tipo restituito. I tipi di argomento rappresentati da type1
vengono automaticamente curried. Ciò suggerisce che per questo tipo si usa un modulo di tupla se gli argomenti della funzione di destinazione sono curried e una tupla racchiusa tra parentesi per gli argomenti già presenti nel formato tupla. Il currying automatico rimuove un set di parentesi, lasciando un argomento di tupla corrispondente al metodo di destinazione. Fare riferimento all'esempio di codice per la sintassi da usare in ogni caso.
I delegati possono essere collegati ai valori della funzione F# e ai metodi statici o di istanza. I valori delle funzioni F# possono essere passati direttamente come argomenti ai costruttori delegati. Per un metodo statico, si costruisce il delegato usando il nome della classe e il metodo . Per un metodo di istanza, specificare l'istanza dell'oggetto e il metodo in un solo argomento. In entrambi i casi viene usato l'operatore di accesso ai membri (.
).
Il Invoke
metodo sul tipo delegato chiama la funzione incapsulata. Inoltre, i delegati possono essere passati come valori di funzione facendo riferimento al nome del metodo Invoke senza le parentesi.
Il codice seguente illustra la sintassi per la creazione di delegati che rappresentano vari metodi in una classe . A seconda che il metodo sia un metodo statico o un metodo di istanza e che contenga argomenti nella forma tupla o nel formato curried, la sintassi per dichiarare e assegnare il delegato è leggermente diversa.
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)
Il codice seguente illustra alcuni dei diversi modi in cui è possibile usare i delegati.
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
L'output dell'esempio di codice precedente è il seguente.
aaaaa
bbbbb
ccccc
[|"aaa"; "bbb"|]
I nomi possono essere aggiunti ai parametri delegati in questo modo:
// 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
I nomi dei parametri delegati sono facoltativi e verranno visualizzati nel Invoke
metodo . Non è necessario che corrispondano ai nomi dei parametri nell'implementazione. Sono consentiti solo per la forma curried ma non per il formato 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")
L'output dell'esempio di codice precedente è il seguente.
aa