28 Stimmen

Was ist der Unterschied zwischen Delegaten in C# und Funktionen als Werte erster Klasse in F#?

Genauer gesagt, was sind die Eigenschaften (wenn überhaupt), die Delegaten haben, dass Funktionen als Werte erster Klasse in F# nicht haben; und was sind die Eigenschaften, die Funktionen als Werte erster Klasse haben (wenn überhaupt), dass Delegaten in C # nicht haben?

31voto

Christian Klauser Punkte 4316

Delegates und F# "First class function values" sind recht unterschiedlich.

Delegate sind ein Mechanismus der CLR, ein typsicherer Wrapper um Funktions-Zeiger+Objekt-Paare (zum Beispiel Methoden, die this -Zeiger wird zusammen mit der Methodenadresse erfasst).

F#-Funktionswerte hingegen sind die Implementierung einer abstrakten Klasse FSharpFunc<,> (früher hieß sie FastFunc<,> vor der offiziellen Veröffentlichung von F#). Der Aufruf erfolgt über normale virtuelle Methoden, was viel schneller ist als der Aufruf von Delegaten. Das ist der Grund, warum das F#-Team Delegates nicht von vornherein verwendet hat.

Wenn Sie also Funktionen als Werte erster Klasse über abstrakte Klassen/virtuelle Methoden "implementieren" können, warum hat Microsoft Delegierte hinzugefügt?

  • Es gab keine Alternative In .NET 1.0/1.1 gab es keine Generika, so dass Sie für jede Funktionssignatur, die Sie verwenden wollten, einen neuen Delegattyp (="Funktionstyp") definieren mussten.
  • (Nein, die Verwendung von Schnittstellen wie in Java zählt nicht. :-P )

Ok, aber wir haben Generics seit .NET 2.0, warum haben wir noch Delegates? Warum können wir nicht einfach Func<,> et Action<> für alles?

  • Abwärtskompatibilität
  • Multicast-Delegierte Delegierte können miteinander verkettet werden, um neue Delegierte zu bilden. Dieser Mechanismus wird zur Implementierung von Ereignissen in VB.NET und C# verwendet. Hinter den Kulissen ist ein Ereignis eigentlich nur ein einzelnes Delegatenfeld. Die Verwendung des += Syntax fügen Sie im Wesentlichen Ihren Event-Handler-Delegaten in die Kette der Delegierten im Ereignisfeld ein.

Gibt es, abgesehen von Ereignissen, einen Grund, Delegierte zu verwenden, anstatt FSharpFunc<,>

Ja, eine: Jede einzelne Umsetzung von FSharpFunc<,> , die Lambda-Ausdrücke* enthält, ist eine neue Klasse. Und in .NET werden Klassen in den Metadaten der kompilierten Assembly kodiert. Delegaten hingegen benötigen keine zusätzlichen Metadaten. Der Delegat Typen tun aber Instanziierung Diese Delegatentypen sind frei in Bezug auf Metadaten.

Aber Moment, sind nicht auch C#-Lambda-Ausdrücke/anonyme Methoden als versteckte Klassen implementiert?

Ja, C#-Lambdas nehmen das Schlimmste aus beiden Welten ^^

7voto

thr Punkte 18660

Ich wollte nur hinzufügen, dass diese Aussage von SealedSun nicht wahr ist:

Der Aufruf erfolgt über die normale virtuelle Methoden, was viel schneller ist als der Aufruf von Delegaten. Das ist der Grund, warum das F#-Team keine Delegaten verzichtet hat.

F#-Funktionen sind nicht schneller als der Aufruf von Delegaten, vielleicht war das in .NET 1.0 der Fall, aber heute sind der Aufruf von Delegaten und der Aufruf von virtuellen Methoden ziemlich gleichauf.

Auch das Aufrufen von F#-Funktionen, die nicht statisch vom Compiler gebunden werden können, ist im Vergleich zum Aufrufen eines Delegaten sehr langsam.

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

Erzeugt auf meinem Computer folgende Ergebnisse

delegate call: 65ms
fsharp func (staticly linked): 4ms
fsharp func (dynamic invoke): 356ms

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X