デリゲート (F#)

デリゲートによって、関数呼び出しはオブジェクトとして表されます。 通常、F# では、関数をファーストクラスの値として表すために関数の値を使用する必要があります。ただし、デリゲートは .NET Framework で使用され、それを予期する API と相互運用するときに必要になります。 また、他の .NET Framework 言語から使用するように設計されたライブラリを作成するときにも使用できます。

構文

type delegate-typename = delegate of type1 -> type2

解説

前の構文では、type1 は引数の型を表し、type2 は戻り値の型を表します。 type1 によって表される引数の型は、自動的にカリー化されます。 これが示唆するのは、この型では、対象の関数の引数がカリー化されている場合はタプル形式を使用し、既にタプル形式の引数にはかっこで囲まれたタプルを使用するということです。 自動カリー化により、一連のかっこが削除され、対象のメソッドに一致するタプル引数が残されます。 各ケースで使用する構文については、コード例を参照してください。

デリゲートは、F# 関数の値、および静的またはインスタンス メソッドにアタッチできます。 F# 関数の値は、引数としてデリゲート コンストラクターに直接渡すことができます。 静的メソッドの場合は、クラスの名前とメソッドを使用してデリゲートを構築します。 インスタンス メソッドの場合は、1 つの引数にオブジェクト インスタンスとメソッドを指定します。 どちらの場合も、メンバー アクセス演算子 (.) が使用されます。

デリゲート型の 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

関連項目