10

F# 中的可区分联合被编译为抽象类,其选项成为嵌套的具体类。

type DU = A | B

DU 是抽象的,而 DU.A 和 DU.B 是具体的。

使用 ServiceStack,可以使用函数自定义类型到 JSON 字符串和返回的序列化。关于 DU 类型,以下是我在 C# 中的操作方法。

using ServiceStack.Text;

JsConfig<DU.A>.SerializeFn = v => "A"; // Func<DU.A, String>
JsConfig<DU.B>.SerializeFn = v => "B"; // Func<DU.B, String>
JsConfig<DU>.DeserializeFn = s =>
    if s == "A" then DU.NewA() else DU.NewB(); // Func<String, DU>

F# 是否知道它的可区分联合的编译形式?如何在编译时获得 F# 中的 DU.A 类型?

typeof<DU> // compiles
typeof<DU.A> // error FS0039: The type 'A' is not defined
typeof<A> // error FS0039: The type 'A' is not defined

我可以很容易地在 F# 中注册一个反序列化函数。

open System
open ServiceStack.Text

JsConfig<DU>.RawDeserializeFn <-
    Func<_, _>(fun s -> printfn "Hooked"; if s = "A" then A else B)

是否可以在 F# 中为具体类型 DU.A 和 DU.B 完全注册序列化函数?

4

3 回答 3

10

虽然所有行为(抽象类等)不仅仅是一个实现细节,它实际上是由规范定义的,这些东西不能从 F# 访问 - 这是规范的引用

编译的联合类型 U 具有:

· 每个空联合案例 C 都有一个 CLI 静态 getter 属性 UC。此属性获取一个代表每个此类案例的单例对象。

· 一个 CLI 嵌套类型 UC 用于每个非空联合案例 C。此类型具有联合案例的每个字段的实例属性 Item1、Item2....,如果只有一个字段,则具有单个实例属性 Item。但是,只有一种情况的编译联合类型没有嵌套类型。相反,联合类型本身扮演了案例类型的角色。

· 一个 CLI 静态方法 U.NewC 用于每个非空联合案例 C。此方法为该案例构造一个对象。

· 每个案例 C 有一个 CLI 实例属性 U.IsC。此属性为案例返回 true 或 false。

· 每个案例 C 有一个 CLI 实例属性 U.Tag。此属性获取或计算对应于案例的整数标记。

· 如果 U 有多个 case,它有一个 CLI 嵌套类型 U.Tags。U.Tags 类型为每种情况包含一个整数文字,从零开始按递增顺序排列。

· 除了任何用户定义的属性或方法之外,编译的联合类型还具有实现其自动生成的接口所需的方法。

这些方法和属性不能直接从 F# 中使用。但是,这些类型具有面向用户的 List.Empty、List.Cons、Option.None 和 Option.Some 属性和/或方法。

重要的是,“这些方法和属性不能从 F# 中使用”。

于 2013-07-24T11:51:06.473 回答
6

丹尼尔是正确的,您可以通过为基类型注册序列化函数来做到这一点DU。这是一个更完整的例子

open System
open ServiceStack.Text

type DU = A | B

let serialize = function
    | A -> "A"
    | B -> "B"

let deserialize = function
    | "A" -> A
    | "B" -> B
    | _   -> failwith "Can't deserialize"

JsConfig<DU>.SerializeFn <- Func<_,_>(serialize)
JsConfig<DU>.DeSerializeFn <- Func<_,_>(deserialize)

let value = [| A; B |]
let text = JsonSerializer.SerializeToString(value)
let newValue = JsonSerializer.DeserializeFromString<DU[]>(text)

结果:

val value : DU [] = [|A; B|]
val text : string = "["A","B"]"
val newValue : DU [] = [|A; B|]
于 2013-07-24T16:18:39.953 回答
5

F# 中的 DU 是单一类型这一事实是其有用性的关键。F# 方法是使用模式匹配:

JsConfig<DU>.SerializeFn <- function
  | A -> "A"
  | B -> "B"

应该可以工作,因为联合案例不仅是 C# 中的嵌套类型,也是子类型。当然,如果 ServiceStack 不考虑基本类型的序列化器,那么这将不起作用。

于 2013-07-24T14:16:23.117 回答