Quelle est la différence entre les délégués en C# et les fonctions en tant que valeurs de première classe en F#?

Plus précisément, quelles sont les caractéristiques (le cas échéant) que les délégués ont qui fonctionnent comme des valeurs de première classe dans F# n'ont pas; et quelles sont les caractéristiques qui fonctionnent comme des valeurs de première classe ont (le cas échéant) que les délégués en C# n'ont pas?

26
demandé sur Steve Ellinger 2010-10-09 23:24:41

2 réponses

Les délégués et F# "valeurs de fonction de première classe" sont assez différents.

Les délégués sont un mécanisme du CLR, un wrapper sécurisé autour des paires fonction-pointeur + objet(par exemple les méthodes, le this-pointeur est capturé avec l'adresse de la méthode).

Les valeurs de fonction F# d'autre part, sont l'implémentation d'une classe abstraite FSharpFunc<,> (elle était appelée FastFunc<,> avant la sortie officielle de F#). L'Invocation se fait via des méthodes virtuelles ordinaires, ce qui est beaucoup plus rapide que l'invocation de délégué. C'est la raison pour laquelle L'équipe F#n'a pas utilisé de délégués en premier lieu.

Donc, si vous pouvez "implémenter" des fonctions en tant que valeurs de première classe via des classes abstraites/méthodes virtuelles, Pourquoi Microsoft a-t-il ajouté des délégués?

  • Il N'y avait pas d'alternative Dans. net 1.0 / 1.1, il n'y avait pas de génériques, donc vous deviez définir un nouveau type de délégué (="type de fonction") pour chaque signature de fonction que vous vouliez utiliser.
  • (non, juste en utilisant des interfaces comme en Java ne compte pas. :- P)

Ok, mais nous avons des génériques depuis. net 2.0, pourquoi avons-nous encore des délégués? Pourquoi ne pouvons-nous pas simplement utiliser Func<,> et Action<> pour tout?

  • compatibilité ascendante
  • délégués Multicast Les délégués peuvent être enchaînés pour former de nouveaux délégués. Ce mécanisme est utilisé pour implémenter des événements dans VB.NET et C#. Dans les coulisses, un événement n'est vraiment qu'un seul champ délégué. En utilisant la syntaxe += vous essentiellement ajoutez votre event-handler-delegate à la chaîne de délégués dans le champ event.

En dehors des événements, y a-t-il une raison d'utiliser les délégués FSharpFunc<,>

Oui, Un: chaque implémentation de FSharpFunc<,>, qui inclut lambda-expressions*, est une nouvelle classe. Et dans. NET Les classes sont codées dans les métadonnées de l'assembly compilé. En revanche, les délégués n'ont pas besoin de métadonnées supplémentaires. Le délégué types de faire, mais instanciation ces types délégués est libre en termes de métadonnées.

Mais attendez, les expressions c # lambda / méthodes anonymes ne sont-elles pas trop implémentées en tant que classes cachées?

Oui, c # lambdas prennent le pire des deux mondes ^ ^

28
répondu Christian Klauser 2010-10-09 20:02:11

Je voulais juste ajouter que cette déclaration de SealedSun n'est pas vraie:

L'Invocation se passe via ordinaire méthodes virtuelles, qui est beaucoup plus rapide que l'invocation de délégué. C'est l' raison pour laquelle L'équipe F#n'a pas utilisé les délégués à la première place.

Les fonctions F# ne sont pas plus rapides que l'invocation de délégué, peut-être que c'était le cas dans.net 1.0, mais maintenant une invocation de délégué de jours et l'invocation de méthodes virtuelles sont à peu près à égalité.

Invoquant également Les fonctions F# qui ne peuvent pas être liées statiquement par le compilateur sont très lentes par rapport à l'appel d'un délégué.

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

Produit les résultats suivants sur mon ordinateur

delegate call: 65ms
fsharp func (staticly linked): 4ms
fsharp func (dynamic invoke): 356ms
6
répondu thr 2010-10-17 19:47:29