复制受歧视的联合对象

时间:2015-04-28 22:40:13

标签: reflection f# discriminated-union

我想制作一个具有歧视联合类型的对象的副本,其中一个或两个特定字段分配不同的值,并且任何其他字段直接复制。

棘手的部分是我正在尝试编写一个函数来执行此操作,即使将更多情况添加到联合中,这将保持不变,因此我无法使用match;相反,我正在寻找一种使用反射来检查特定情况的字段的解决方案。这是我到目前为止反面的,从一个对象中提取值,而不管它的确切类型:

let case a =
    match FSharpValue.GetUnionFields (a, typeof<Term>) with
    | info, _ ->
        info

let unpack a =
    let fields = List.ofSeq ((case a).GetFields ())
    List.collect
        (fun (field: PropertyInfo) ->
            let t = field.PropertyType
            if t = typeof<Term> then
                [field.GetValue a :?> Term]
            elif t.IsGenericType && t.GenericTypeArguments.[0] = typeof<Term> then
                field.GetValue a :?> Term list
            else
               []
        )
        fields

我正在尝试编写一个类似于以下内容的函数:

let pack a xs =
    let fields = List.ofSeq ((case a).GetFields ())
    ...

我尝试使用MemberwiseClone但是受保护,所以不能在子类之外使用。我可能正在寻找像'创建这种类型的新对象,然后逐步复制字段或填写适当的值',尽管我不太确定'这种类型'是什么,因为{{1对于受歧视的工会不起作用。什么是最好的方法呢?

1 个答案:

答案 0 :(得分:5)

这实际上比看起来容易得多(忽略反射的常见问题)。

您已使用FSharpValue.GetUnionFields获取有关union案例的信息并获取当前union的值。还有FSharpValue.MakeUnion可以获取这些信息并为您提供联合值:

type Term = 
  | Foo of int * string * bool

let a = Foo(42, "answer", false)

let case, values = FSharpValue.GetUnionFields(a, typeof<Term>) 
values.[2] <- box true
FSharpValue.MakeUnion(case, values) :?> Term