Delegaten (F#)

Ein Delegat stellt einen Funktionsaufruf als Objekt dar. In F# sollten normalerweise Funktionswerte verwendet werden, um Funktionen als erstklassige Werte darzustellen. Delegaten werden allerdings in .NET Framework verwendet und sind somit erforderlich, wenn Sie mit APIs arbeiten, von denen sie erwartet werden. Sie können auch bei der Erstellung von Bibliotheken verwendet werden, die für die Verwendung über andere .NET Framework-Sprachen konzipiert sind.

Syntax

type delegate-typename = delegate of type1 -> type2

Bemerkungen

In der vorherigen Syntax stellt type1 den Argumenttyp bzw. die Argumenttypen und type2 den Rückgabetyp dar. Für die durch type1 dargestellten Argumenttypen wird automatisch Currying verwendet. Das bedeutet, dass Sie für diesen Typ eine Tupelform verwenden, wenn Currying für die Argumente der Zielfunktion verwendet wird, bzw. für Argumente, die bereits in Tupelform vorliegen, ein Tupel mit Klammern. Das automatische Currying entfernt eine Reihe von Klammern, sodass ein Tupelargument zurückbleibt, das der Zielmethode entspricht. Die Syntax, die Sie jeweils verwenden sollten, finden Sie im Codebeispiel.

Delegaten können an F#-Funktionswerte und statische Methoden oder Instanzmethoden angefügt werden. F#-Funktionswerte können direkt als Argumente an Konstruktoren übergeben werden. Bei einer statischen Methode erstellen Sie den Delegaten unter Verwendung des Namens der Klasse und der Methode. Für eine Instanzmethode geben Sie die Objektinstanz und die Methode in einem einzelnen Argument an. In beiden Fällen wird der Memberzugriffsoperator (.) verwendet.

Die Invoke-Methode für den Delegattyp ruft die gekapselte Funktion auf. Außerdem können Delegaten als Funktionswerte übergeben werden, indem auf den Namen der Invoke-Methode ohne die Klammern verwiesen wird.

Der folgende Code zeigt die Syntax zum Erstellen von Delegaten, die verschiedene Methoden in einer Klasse darstellen. Je nachdem, ob es sich bei der Methode um eine statische Methode oder um eine Instanzmethode handelt und ob sie Argumente in Tupelform oder in Curry-Form aufweist, unterscheidet sich die Syntax zum Deklarieren und Zuweisen des Delegaten etwas.

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)

Der folgende Code zeigt einige der verschiedenen Verwendungsmöglichkeiten für Delegaten:

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

Das obige Codebeispiel gibt Folgendes aus:

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

Namen können Stellvertretungsparametern wie folgt hinzugefügt werden:

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

Namen für Stellvertretungsparameter sind optional und werden in der Methode Invoke angezeigt. Sie müssen nicht mit den Parameternamen in der Implementierung übereinstimmen. Sie sind nur für die geschweifte Form, aber nicht für die tuplierte Form zulässig.

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")

Das obige Codebeispiel gibt Folgendes aus:

aa

Weitere Informationen