【问题标题】:Unboxing values of F# discriminated unionsF# 可区分联合的拆箱值
【发布时间】:2017-12-27 11:48:41
【问题描述】:

即使键入了基础值,我的 F# 代码的某些函数也会接收装箱为对象的值。如果该值是可区分的联合,则无法将其拆箱回其 F# 类型。这是一个简单的例子:

type Result<'TOk,'TError> = 
| Ok of 'TOk 
| Error of 'TError

type ResultA = Result<string, int>

let a = Ok "A"
let o = box a

match o with
| :? ResultA -> printfn "match ResultA"
// | :? ResultA.Ok -> printfn "match" // doesn't compile
| _ when o.GetType().DeclaringType = typedefof<ResultA> -> printfn "match via reflection"
| _ -> printfn "no match"

此示例的输出是“通过反射匹配”,ResultA 永远不会匹配,因为装箱的值是不同的 CLR 类型 - Result.Ok。由于 F# 可区分联合案例表示为其自己的类型,因此装箱值与类型 ResultA 不匹配。此外,不可能将其与 ResultA.OK 匹配,因为在 F# 代码中它不是合法类型。唯一的选择似乎是使用反射手动实例化一个值,这既低效又愚蠢,因为该值已经实例化,它就在这里,一旦装箱就无法在 F# 代码中访问它。

我是否忽略了什么?有没有更直接的方法来拆箱 F# 区分联合值?

【问题讨论】:

    标签: reflection f# discriminated-union


    【解决方案1】:

    你只是匹配了一个不同的类型。您的变量a 不是ResultA 类型的,而是泛型类型Result&lt;string, 'a&gt;,当被装箱时会被强制转换为Result&lt;string, obj&gt;。

    要么明确地使变量具有正确的类型:

    let a : ResultA = Ok "A"
    

    或者匹配正确的类型:

    match o with
    | :? Result<string, obj> -> printfn "match ResultA"
    

    这两个选项都可以。


    关于你的假设的注释:

    ResultA 永远不会匹配,因为装箱的值属于不同的 CLR 类型 - Result.Ok

    这不是原因。与类型匹配的工作方式与 C# 中的 is/as 运算符相同 - 即它匹配子类型以及确切类型。并且 DU 成员被编译为 DU 类型本身的子类型。这就是 F# 如何让 .NET 将不同情况作为一种类型来处理。

    关于运行时输入的一般说明:
    不需要在运行时处理类型。如果可能的话,尽量避免它。经验法则应该是,如果您发现自己在运行时处理类型,那么您可能建模错误。

    如果您不知道确切一切如何运作,则尤其如此。

    【讨论】:

    • 谢谢费奥多尔。实际上,您的第一个建议没有用 - 明确地将变量设置为正确的类型会导致相同的盒装类型并且不匹配。但是第二个起作用了——改变了模式匹配的情况。
    • 您在尝试第一个选项时一定犯了一个错误。它确实有效。
    • 我完全同意你的预防措施。不幸的是,由于外部库的具体情况,我必须处理一些装箱值。
    • @FyodorSoikin - 尝试一下,我在 FSI 中输入了 a 并得到了 val a : Result&lt;string,'a&gt;。它不显示字符串“A”。你知道这是为什么吗?
    • @Soldalma 这是正常的 FSI 行为:当您评估 let-binding 时,它只显示类型。尝试评估绑定,然后只输入a。
    猜你喜欢
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 1970-01-01
    • 2014-09-13
    • 2012-12-14
    相关资源
    最近更新 更多