Génération de code des expressions lambda C # et F #

Regardons le code généré par F # pour une fonction simple:

let map_add valueToAdd xs = xs |> Seq.map (fun x -> x + valueToAdd) 

Le code généré pour l’expression lambda (instance de la valeur fonctionnelle F #) ressemblera à ceci:

 [Serializable] internal class map_add@3 : FSharpFunc { public int valueToAdd; internal map_add@3(int valueToAdd) { this.valueToAdd = valueToAdd; } public override int Invoke(int x) { return (x + this.valueToAdd); } } 

Et regardez à peu près le même code C #:

 using System.Collections.Generic; using System.Linq; static class Program { static IEnumerable SelectAdd(IEnumerable source, int valueToAdd) { return source.Select(x => x + valueToAdd); } } 

Et le code généré pour l’expression lambda C #:

 [ComstackrGenerated] private sealed class c__DisplayClass1 { public int valueToAdd; public int b__0(int x) { return (x + this.valueToAdd); } } 

J’ai donc quelques questions:

  • Pourquoi la classe générée par F # n’est-elle pas marquée comme sealed ?
  • Pourquoi la classe générée par F # contient-elle des champs publics puisque F # n’autorise pas les fermetures mutables?
  • Pourquoi la classe générée par F # a-t-elle un constructeur? Il peut être parfaitement initialisé avec les champs publics …
  • Pourquoi la classe générée en C # n’est-elle pas marquée en tant que [Serializable] ? Les classes générées pour les expressions de séquence F # sont également devenues [Serializable] , contrairement aux classes pour les iterators C #.

Comme ils sont générés par le compilateur, les problèmes concernant les champs public / scellé sont un peu discutables – vous ne devriez jamais le voir sauf via les outils de débogage – comment le sous-classeriez-vous ou le muteriez-vous, sauf en contournant le compilateur? Si vous avez ce niveau d’access de débogage, vous pouvez le muter quand même (via reflection).

Pour C #, il faut que top soit un champ permettant une certaine utilisation de ref / out et permettant une utilisation correcte avec les structures mutables capturées (oui, mal, nous le soaps). Je suppose que F # est similaire ici (pouvez-vous muter un sous- [sous- [sous -]] membre de la valeur capturée?). Les membres pourraient probablement être internes, cependant.

Re [Serialziable] ; pourquoi quelque chose qui sous-tend une fermeture serait-il sérialisable? Les delegates font des candidats extrêmement pauvres en sérialisation. Peut-être que la nature de F # signifie qu’il est préférable de conserver une opération (stream intermédiaire) sur le disque – mais en général, je ne le recommanderais pas. Je ne m’attendrais pas à ce que ces objects (iterators et classes de capture) soient sérialisables.

Pourquoi la classe générée par F # n’est-elle pas marquée comme scellée?

Parce que le compilateur ne le fait pas (c’est finalement un choix de génération de code.

Pourquoi la classe générée par F # contient-elle des champs publics puisque F # n’autorise pas les fermetures mutables?

Vous devez pouvoir accéder à une référence à une instance pour la modifier. Mais vous ne pouvez pas, alors être modifiable n’a pas d’importance. Et cela évite probablement de devoir recourir à un cas spécial dans lequel un mutable est capturé dans une fermeture.

Pourquoi la classe générée par F # a-t-elle un constructeur? Il peut être parfaitement initialisé avec les champs publics …

Encore une fois le choix de génération de code.

Pourquoi la classe générée en C # n’est-elle pas marquée en tant que [Serializable]? Les classes générées pour les expressions de séquence F # sont également devenues [Serializable], contrairement aux classes pour les iterators C #.

Plus de choix de génération de code.

Aucun de ces choix n’est visible par le développeur (c’est-à-dire qu’il ne fait aucune différence pour le code client), cela ne fait vraiment aucune différence.