C#和F#lambda表达式代码生成

让我们看看F#为简单函数生成的代码:

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

生成的lambda表达式代码(F#function值的实例)如下所示:

 [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); } } 

看看几乎相同的C#代码:

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

并为C#lambda表达式生成代码:

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

所以我有一些问题:

  • 为什么F#生成的类没有标记为sealed
  • 为什么F#生成的类包含公共字段,因为F#不允许可变闭包?
  • 为什么F#生成的类有一个构造函数? 它可以用公共领域完美初始化……
  • 为什么C#生成的类没有标记为[Serializable] ? 此外,为F#序列表达式生成的类也变为[Serializable] ,而C#迭代器的类则不会。

由于它们是编译器生成的,密封/公共字段问题有点没有实际意义 – 除了通过调试工具之外你不应该看到它 – 除了通过单独编译器之外,你将如何inheritance或改变它? 如果您具有级别的调试访问权限,则无论如何都可以对其进行变更 (通过reflection)。

对于C#,它需要top为一个字段以允许某些ref / out使用,并允许正确使用捕获的可变结构(是的,邪恶,我们知道)。 我假设F#在这里类似(你可以改变捕获值的子[sub- [sub – ]]成员吗?)。 但成员可能是内部成员。

Re [Serialziable] ; 为什么支持闭包的东西可以序列化? 代表们制作了极差的序列化候选人。 也许F#的本质意味着它更适合于将操作(中间流)持久化到磁盘 – 但总的来说,我不推荐它。 我不希望这些对象(迭代器和捕获类)是可序列化的。

为什么F#生成的类没有标记为密封?

因为编译器没有(这到底是代码生成的选择。

为什么F#生成的类包含公共字段,因为F#不允许可变闭包?

您需要能够获取对实例的引用以进行修改。 但你不能,所以可修改无所谓。 并且这可能避免需要在闭包中捕获mutable的特殊情况。

为什么F#生成的类有一个构造函数? 它可以用公共领域完美初始化……

再次代码生成选择。

为什么C#生成的类没有标记为[Serializable]? 此外,为F#序列表达式生成的类也变为[Serializable],而C#迭代器的类则不会。

更多代码生成选择。

这些选择都不是开发人员可见的(即对客户端代码没有任何影响),它确实没有任何区别。