为什么 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 序列化很酷时它会是一个选项:-))。
为什么 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 序列化很酷时它会是一个选项:-))。