让我们看一下F#为简单函数生成的代码:
let map_add valueToAdd xs =
xs |> Seq.map (fun x -> x + valueToAdd)
生成的lambda表达式代码(F#功能值的实例)如下所示:
[Serializable]
internal class map_add@3 : FSharpFunc<int, int> {
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<int> SelectAdd(IEnumerable<int> source, int valueToAdd) {
return source.Select(x => x + valueToAdd);
}
}
生成的C#lambda表达式代码:
[CompilerGenerated]
private sealed class <>c__DisplayClass1 {
public int valueToAdd;
public int <SelectAdd>b__0(int x) { return (x + this.valueToAdd); }
}
所以我有一些问题:
sealed
?[Serializable]
?此外,为F#序列表达式生成的类也变为[Serializable]
,而C#迭代器的类则不会。答案 0 :(得分:4)
由于它们是编译器生成的,所以密封/公共字段问题有点没有意义 - 除了通过调试工具之外你不应该看到它 - 除了通过单独编译器之外,你将如何对它进行子类化或变异?如果您具有 级别的调试访问权限,则无论如何都可以 (通过反射)。
对于C#,它需要最多为字段以允许某些ref
/ out
使用,并允许正确使用捕获的可变结构(是的,邪恶,我们知道)。我假设F#在这里类似(你可以改变捕获值的子[sub- [sub - ]]成员吗?)。但成员可能是内部成员。
Re [Serialziable]
;为什么支持闭包的东西可以序列化?代表们制作了极差的序列化候选人。也许F#的本质意味着它更适合于将操作(中间流)持久化到磁盘 - 但总的来说,我不推荐它。我不希望这些对象(迭代器和捕获类)是可序列化的。
答案 1 :(得分:4)
为什么F#生成的类没有标记为密封?
因为编译器没有(这最终是代码生成选择。
为什么F#生成的类包含公共字段,因为F#不允许可变闭包?
您需要能够获取对实例的引用以进行修改。但你不能,所以可修改无所谓。这可能避免了需要在闭包中捕获mutable的特殊情况。
为什么F#生成的类有一个构造函数?它可以用公共领域完美地初始化......
再次代码生成选择。
为什么C#生成的类没有标记为[Serializable]?此外,为F#序列表达式生成的类也变为[Serializable],而C#迭代器的类则不会。
更多代码生成选择。
这些选择都不是开发人员可见的(即对客户端代码没有任何影响),它确实没有任何区别。