より具体的には、F#のファーストクラスの値として機能するデリゲートが持つ特性(ある場合)は何ですか。また、ファーストクラスの値として機能する特性(ある場合)には、C#のデリゲートにはない特性がありますか?
2 に答える
デリゲートとF#の「ファーストクラス関数の値」はまったく異なります。
デリゲートはCLRのメカニズムであり、関数ポインターとオブジェクトのペアのタイプセーフラッパーです(たとえば、メソッドの場合、-ポインターthis
はメソッドアドレスと一緒にキャプチャされます)。
一方、F#関数の値は、抽象クラスの実装です( F#の公式リリースの前にFSharpFunc<,>
呼び出されていました)。FastFunc<,>
呼び出しは通常の仮想メソッドを介して行われます。これは、デリゲート呼び出しよりもはるかに高速です。これが、F#チームがそもそもデリゲートを使用しなかった理由です。
では、抽象クラス/仮想メソッドを介して関数をファーストクラスの値として「実装」できるのであれば、なぜMicrosoftはデリゲートを追加したのでしょうか。
- 代替手段はありませんでした。.NET1.0/1.1にはジェネリックがなかったため、使用する関数シグネチャごとに新しいデリゲート型(= "関数型")を定義する必要がありました。
- (いいえ、Javaのようにインターフェースを使用するだけではカウントされません。:-P)
わかりました。ただし、.NET 2.0以降にジェネリックがあります。なぜまだデリゲートがあるのですか?Func<,>
なぜ私たちはすべてのためにそしてすべてのために使うことができないAction<>
のですか?
- 下位互換性
- マルチキャストデリゲートデリゲートをチェーン化して、新しいデリゲートを形成できます。このメカニズムは、VB.NETおよびC#でイベントを実装するために使用されます。舞台裏では、イベントは実際には単一のデリゲートフィールドにすぎません。構文を使用すると、
+=
基本的に、event-handler-delegateをイベントフィールドのデリゲートのチェーンに追加します。
イベントとは別に、デリゲートを使用する理由はありますかFSharpFunc<,>
はい、1つ:FSharpFunc<,>
ラムダ式*を含むのすべての実装は新しいクラスです。また、.NETでは、クラスはコンパイルされたアセンブリのメタデータにエンコードされます。一方、デリゲートは追加のメタデータを必要としません。デリゲートタイプは機能しますが、これらのデリゲートタイプのインスタンス化は、メタデータの観点からは無料です。
しかし、待ってください、C#ラムダ式/匿名メソッドも隠しクラスとして実装されていませんか?
はい、C#ラムダは両方の世界で最悪です^^
SealedSunからのこのステートメントは真実ではないことを付け加えたかっただけです。
呼び出しは通常の仮想メソッドを介して行われます。これは、デリゲート呼び出しよりもはるかに高速です。これが、F#チームがそもそもデリゲートを使用しなかった理由です。
F#関数はデリゲート呼び出しよりも高速ではありません。おそらくそれは.NET 1.0の場合でしたが、現在ではデリゲート呼び出しと仮想メソッドの呼び出しはほぼ同等です。
また、コンパイラーによって静的にバインドできないF#関数の呼び出しは、デリゲートの呼び出しに比べて非常に低速です。
open System
open System.Diagnostics
let time name f =
let sw = new Stopwatch()
sw.Start()
f()
sw.Stop()
printfn "%s: %dms" name sw.ElapsedMilliseconds
time "delegate call" (
fun () ->
let f =
new Func<int, int, int>(
fun i1 i2 ->
let y = i1 + i2
let x = y + i1
let z = x + y + i2
z + x + y + i1
)
let mutable r = 0
for i = 0 to 10000000 do
r <- f.Invoke(i, i)
)
let f i1 i2 =
let y = i1 + i2
let x = y + i1
let z = x + y + i2
z + x + y + i1
time "fsharp func (static bound)" (
fun () ->
let mutable r = 0
for i = 0 to 10000000 do
r <- f i i
)
let make f =
let mutable r = 0
for i = 0 to 10000000 do
r <- f i i
time "fsharp func (dynamic bound)" (
fun () -> make f
)
Console.ReadLine() |> ignore
私のコンピュータで次の結果を生成します
delegate call: 65ms
fsharp func (staticly linked): 4ms
fsharp func (dynamic invoke): 356ms