如果它是一个,则将广义类型转换为其基础的区分联合

时间:2016-12-16 02:10:38

标签: casting f# discriminated-union

出于调试目的,我需要更多关于浮点数的详细信息,而不是box给出的,并且我的所有测试代码都是通用的。所以我认为我Result<_, _>并匹配类型以获得有意义的输出。

但是,类型可以是选项类型或// Illustrative example of what I'm trying to achieve (I think) let rec mkStr v = match box v with | :? double as dbl -> Str.fromFloat dbl | :? int64 as i -> i.ToString() | _ -> match v :> obj with | :? Result<_, _> as res -> // this will never succeed match res with | Success s -> mkStr s | Fail _ -> "Fail" | x -> x.ToString() 类型(成功/失败)。当你把它们当作有区别的联合对象时,这些类型只是模式匹配,但如果我对它们进行打字测试,它们就不会被击中:

Success

我尝试了各种拳击/拆箱/铸造,以上就是这些品种之一。当我调试时,我可以清楚地看到Result正在通过,调试器甚至不会显示它是Success。我知道DU中的每个鉴别器本身都是一个类型,所以我理解发生了什么,但我怎么能告诉编译器&#34;将其视为&#34;如果它看到它与Failprintf "%A"

中的一个匹配,则为DU

TLDR:给定一个通用类型,如果有的话,如何测试并将类型转换为基础DU?

(PS:我意识到这通常不是杜鹃的方式,但就像{{1}}存在一样,我认为偶尔需要这样做,就像在这种情况下一样)

1 个答案:

答案 0 :(得分:3)

使用引用来获得@kvb在评论中发布的模式匹配支持的解决方案是一个很好的技巧。我过去遇到了类似的问题并使用了一个稍微不同的技巧 - 而不是使用反射来解构和检查值,您可以使用反射来调用具有正确类型参数的泛型方法。这是一个最小的例子:

type MakeString = 
  static member Make<'T>(o:'T) =
    match box o with
    | :? float as f -> sprintf "Float: %A" f
    | o ->
        if typeof<'T>.Name = "FSharpOption`1" then
          let tys = typeof<'T>.GetGenericArguments()
          typeof<MakeString>.GetMethod("MakeOption")
            .MakeGenericMethod(tys).Invoke(null, [| o |]) :?> string
        else
          failwith "Unknown"

  static member MakeOption<'T>(o:option<'T>) =
    match o with
    | None -> "None"
    | Some v -> "Some " + MakeString.Make(v)

MakeString.Make(Some 3.14)

如果Make的某个option<'T2>的某个'T2类型的值被我们无法静态地知道,那么我们会通过查看通用内容来查找'T2使用反射输入类型参数然后我们用MakeOption作为类型参数调用'T2 - 所以在MakeOption内,我们现在(再次)知道选项中包含的值的静态类型。

这样做的一个可能的优点是它都可以使用静态类型,因此当值表示为null时它将自由工作(因此直接调用None.GetType()会引发异常) 。 previous answer posted by @kvb也处理这个问题,但在这种情况下使用特殊情况。