Delegati (F#)
Un delegato rappresenta una chiamata di funzione come un oggetto. In F# in genere i valori di funzione vengono utilizzati per rappresentare le funzioni come valori di prima classe. I delegati vengono tuttavia utilizzati in .NET Framework e pertanto sono necessari quando si interagisce con API che li prevedono. Possono inoltre essere utilizzati in caso di creazione di librerie progettate per l'utilizzo da parte di altri linguaggi .NET Framework.
type delegate-typename = delegate of type1 -> type2
Note
Nella sintassi precedente type1 rappresenta il tipo o i tipi di argomenti e type2 rappresenta il tipo restituito. I tipi di argomenti rappresentati da type1 vengono sottoposti automaticamente a currying. Per questo tipo è pertanto consigliabile utilizzare un formato tupla se gli argomenti della funzione di destinazione sono sottoposti a currying e una tupla tra parentesi per argomenti che sono già in formato tupla. Il currying automatico comporta la rimozione di un set di parentesi, lasciando un argomento della tupla che corrisponde al metodo di destinazione. Fare riferimento all'esempio di codice per la sintassi da utilizzare in ogni caso.
È possibile associare delegati a valori di funzione e a metodi statici o di istanza F#. È possibile passare valori di funzione F# direttamente come argomenti a costruttori di delegati. Per un metodo statico, il delegato viene costruito utilizzando il nome della classe e del metodo. Per un metodo di istanza, vengono forniti l'istanza dell'oggetto e il metodo in un argomento. In entrambi i casi, viene utilizzato l'operatore di accesso ai membri (.).
Il metodo Invoke nel tipo delegato chiama la funzione incapsulata. È possibile inoltre passare delegati come valori di funzione facendo riferimento al nome del metodo Invoke senza le parentesi.
Nel codice seguente viene illustrata la sintassi per la creazione di delegati che rappresentano diversi metodi di una classe. In base al fatto che il metodo sia un metodo statico o un metodo di istanza, e che disponga di argomenti in formato tupla o sottoposto a currying, la sintassi per la dichiarazione e l'assegnazione del 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 = 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)
Nel codice riportato di seguito vengono illustrate alcune delle diverse modalità per l'utilizzo di 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 = 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
Di seguito è riportato l'output dell'esempio di codice precedente.
aaaaa
bbbbb
ccccc
[|"aaa"; "bbb"|]