为什么 F# 闭包是可序列化的?

Why are F# closures serializable?

为什么 F# 闭包具有 [<Serializable>] 属性?什么是实际用例?

此代码演示了闭包的序列化和反序列化:

open System
open System.IO
open System.Runtime.Serialization.Formatters.Binary

let formatter = BinaryFormatter ()
let addTwo = (+) 2

(addTwo.GetType ()).GetCustomAttributes false
|> Array.iter (fun attribute -> 
    printfn "Closure has attribute: %s" (attribute.GetType ()).Name
)

let serializedClosure =
    use memStream = new MemoryStream ()
    formatter.Serialize (memStream, addTwo)
    memStream.ToArray ()

Array.length serializedClosure
|> printfn "Serialized closure length: %d bytes"

let deserializedClosure =
    use memStream = new MemoryStream (serializedClosure)
    formatter.Deserialize memStream :?> Int32 -> Int32

deserializedClosure 3
|> printfn "Invoking deserialized closure: 2 + 3 = %d"

printfn "Press any key to exit..."
Console.ReadKey true |> ignore

这是可行的,因为闭包被编译成 class(注意 serializable 关键字:

.class nested assembly auto ansi serializable beforefieldinit addTwo@6
    extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, int32>
{
    .method assembly specialname rtspecialname instance void .ctor () cil managed {...}
    .method public strict virtual instance int32 Invoke (int32 y) cil managed {...}
}

由于闭包名称(如 addTwo@6)完全依赖于编译器并且即使只修改一点代码也会改变,我想知道使闭包可序列化背后的基本原理是什么?

我认为答案是将 class 标记为 serializable 没有任何负面影响,可以安全地忽略它。不将 class 标记为 serializable 将无法对其进行序列化(如果有人发现它有很好的用途)。因此,编译器只是选择了一个可能更有用的默认值。

这个有实际用途吗?好吧,如果你在多台机器上有相同的二进制 运行 并使用二进制序列化相互通信,那么你 可以 实际上以这种方式传递函数。我认为实际上没有人这样做(MBrace 使用更复杂的机制),但它会是一个选项(或者,当二进制 .NET 序列化很酷时它会是一个选项:-))。