Делегаты (F#)

Делегат представляет вызов функции в качестве объекта. В F# обычно следует использовать значения функций для представления функций в качестве значений первого класса; однако делегаты используются в платформа .NET Framework и поэтому необходимы при взаимодействии с API, ожидающими их. Они также могут использоваться при создании библиотек, предназначенных для использования с других платформа .NET Framework языков.

Синтаксис

type delegate-typename = delegate of type1 -> type2

Замечания

В предыдущем синтаксисе type1 представляет тип аргумента или типы и type2 представляет возвращаемый тип. Типы аргументов, представленные type1 автоматически, курируются. Это означает, что для этого типа используется форма кортежа, если аргументы целевой функции курируются, и скобки для аргументов, которые уже находятся в форме кортежа. Автоматическое курирование удаляет набор скобок, оставляя аргумент кортежа, соответствующий целевому методу. Ознакомьтесь с примером кода для синтаксиса, который следует использовать в каждом случае.

Делегаты могут быть присоединены к значениям функции F#, а также к статическим или экземплярным методам. Значения функции F# можно передавать непосредственно в качестве аргументов для делегатов конструкторов. Для статического метода вы создаете делегат с помощью имени класса и метода. Для метода экземпляра можно указать экземпляр объекта и метод в одном аргументе. В обоих случаях используется оператор доступа к членам (.).

Метод Invoke в типе делегата вызывает инкапсулированную функцию. Кроме того, делегаты могут передаваться в качестве значений функций, ссылаясь на имя метода Invoke без скобок.

В следующем коде показан синтаксис для создания делегатов, представляющих различные методы в классе. В зависимости от того, является ли метод статическим или методом экземпляра, а также имеет ли он аргументы в форме кортежа или в куррионой форме, синтаксис для объявления и назначения делегата немного отличается.

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)

В следующем коде показаны некоторые из различных способов работы с делегатами.

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

Выходные данные предыдущего примера кода приведены следующим образом.

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

Имена можно добавить в параметры делегата, например:

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

Имена параметров делегата являются необязательными и будут отображаться в методе Invoke . Они не требуются для сопоставления имен параметров в реализации. Они разрешены только для куриной формы, но не кортежей формы.

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

Выходные данные предыдущего примера кода приведены следующим образом.

aa

См. также