C# 和 F# lambda 表达式代码生成
让我们看一下由 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); }
}
所以我有一些问题:
- 为什么 F# 生成的类没有标记为
sealed
? - 既然 F# 不允许可变闭包,为什么 F# 生成的类包含 public 字段?
- 为什么 F# 生成的类有构造函数?它可以用公共字段完美地初始化...
- 为什么 C# 生成的类没有标记为
[Serializable]
?此外,为 F# 序列表达式生成的类也变成了[Serializable]
,而为 C# 迭代器生成的类却没有。
Let's look at the code, generated by F# for simple function:
let map_add valueToAdd xs =
xs |> Seq.map (fun x -> x + valueToAdd)
The generated code for lambda expression (instance of F# functional value) will looks like this:
[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); }
}
And look at nearly the same C# code:
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);
}
}
And the generated code for the C# lambda expression:
[CompilerGenerated]
private sealed class <>c__DisplayClass1 {
public int valueToAdd;
public int <SelectAdd>b__0(int x) { return (x + this.valueToAdd); }
}
So I have some questions:
- Why is F#-generated class not marked as
sealed
? - Why does F#-generated class contain public fields since F# doesn't allow mutable closures?
- Why does F# generated class have a constructor? It may be perfectly initialized with the public fields...
- Why is C#-generated class not marked as
[Serializable]
? Also classes generated for F# sequence expressions also became[Serializable]
and classes for C# iterators do not.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
由于它们是编译器生成的,所以密封/公共字段问题有点没有实际意义 - 除了通过调试工具之外,您不应该看到它 - 除了绕过编译器之外,您将如何子类化它或改变它?如果您具有该级别的调试访问权限,您可以无论如何(通过反射)对其进行变异。
对于 C#,它需要 top 是一个字段,以允许某些
ref
/out
使用,并允许正确使用捕获的可变结构(是的,邪恶的) ,我们知道)。我假设 F# 在这里是类似的(你可以改变捕获值的 sub-[sub-[sub-]] 成员吗?)。不过,这些成员可能是内部成员。重新
[Serialziable]
;为什么支撑闭包的东西是可序列化的?代表们是极差的连载候选人。也许 F# 的本质意味着它更适合将操作(中流)持久保存到磁盘 - 但总的来说,我不推荐它。我不期望这些对象(迭代器和捕获类)是可序列化的。Since they are compiler-generated, the sealed / public field issues are a bit moot - you shouldn't ever see it except via debug tools - how would you be subclassing it or mutating it, except by stepping around the compiler? If you have that level of debug access you can mutate it anyway (via reflection).
For C# it needs top be a field to allow certain
ref
/out
usage, and to allow correct usage with captured mutable structs (yes, evil, we know). I assume F# is similar here (can you mutate a sub-[sub-[sub-]]member of the captured value?). The members could probably be internal, though.Re
[Serialziable]
; why would something that underpins a closure be serializable? Delegates make extremely poor serialization candidates. Maybe the nature of F# means that it is better suited to persisting an operation (mid-flow) to disk - but in general, I wouldn't recommend it. I would have no expectation of these objects (iterators and capture-classes) being serialzable.因为编译器不这样做(这最终是代码生成的选择。
您需要能够获取对实例的引用才能对其进行修改。但你不能,所以可修改并不重要。这可能避免了在闭包中捕获可变值的特殊情况。
再次是代码生成选择。
更多代码生成选择。
这些选择都是开发人员可见的(即对客户端代码没有影响),它确实没有区别。
Because the compiler doesn't (this is in the end a code generation choice.
You would need to be able to get to a reference to an instance to modify it. But you can't, so being modifiable doesn't matter. And likely this avoids needing to special case where a mutable is captured in a closure.
Again code generation choice.
More code generation choices.
None of these choices is developer visible (i.e. make no difference to client code) it really makes no difference.