【问题标题】:C# and F# lambda expressions code generationC# 和 F# lambda 表达式代码生成
【发布时间】:2010-06-01 09:30:10
【问题描述】:

让我们看一下代码,由 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# 生成的类包含 public 字段,因为 F# 不允许可变闭包?
  • 为什么 F# 生成的类有构造函数?它可以用公共字段完美初始化...
  • 为什么 C# 生成的类没有标记为[Serializable]?此外,为 F# 序列表达式生成的类也变成了 [Serializable],而 C# 迭代器的类则没有。

【问题讨论】:

    标签: c# serialization f# lambda


    【解决方案1】:

    由于它们是编译器生成的,因此密封/公共字段问题有点没有实际意义 - 除了通过调试工具外,您永远不会看到它 - 除了绕过编译器,您将如何对其进行子类化或变异?如果您有那种级别的调试访问权限,则可以无论如何(通过反射)对其进行变异。

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

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

    【讨论】:

    • 1. sealed 关键字可能允许 JIT 编译器进行一些优化,因为静态已知每个虚拟方法都不能被覆盖。 2. F# 只允许传递另一个byref&lt;ty&gt; 参数和F# 引用单元格作为byref&lt;ty&gt; 参数,因此闭包字段本身在F# 中是不可变的(不是该字段的内容),并且可能是完全私有的。 3. 序列化将允许一些带有延续的疯狂场景,例如将 C# 迭代器状态保存到 SQL 数据库中并恢复执行状态。 F# 支持这一点非常好,但有趣的是为什么不支持 C#...
    • @ControlFlow - (1) 在宏大的计划中相当小。我怀疑它会产生任何明显的差异,但也没有充分的理由不应该被密封,所以我有点同意。 (3) 好吧,面对我的序列化问题,我宁愿以不同的方式解决这个问题。我根本不认为我会非常重视。当然是 YMMV。但是如果不了解所涉及的所有字段,它就不能保证整个图是可序列化的 - 可能因此它没有被标记为这样。
    • 感谢您的回答! (3) - 更接近现实生活场景:C# 代码var t = "hello!"; AppDomain.CreateDomain("test").DoCallBack(() =&gt; Console.WriteLine(t)); 在运行时失败,因为闭包不可序列化,F# 版本将按预期工作......很好,不是吗? :)
    • @ControlFlow - 实际上,AppDomains 之间的远程处理可能是[Serializable] 的主要有效 用途。不过,我不能说我会将此视为一个问题。
    • 肯定闭包应该是MarshalByRefObjects?我发现当他们不这样做时,这是一件很痛苦的事情!
    【解决方案2】:

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

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

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

    您需要能够获得对实例的引用才能对其进行修改。但你不能,所以可修改并不重要。这可能避免了需要在闭包中捕获可变的特殊情况。

    为什么 F# 生成的类有构造函数?它可以用公共字段完美初始化...

    再次选择代码生成。

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

    更多代码生成选择。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 2018-08-23
      • 2011-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多