F# 可区分联合的拆箱值

Unboxing values of F# discriminated unions

我的 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"

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

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

您只是在匹配不同的类型。您的变量 a 不是 类型 ResultA,而是泛型类型 Result<string, 'a>,当装箱时它被强制转换为 Result<string, obj>

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

let a : ResultA = Ok "A"

或匹配正确的类型:

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

两个选项都可以。


关于你的假设的注释:

ResultA is never matched because the boxed value is of a different CLR type - Result.Ok

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

关于运行时键入的一般注意事项:
不必在运行时处理类型。尽可能避免使用它。经验法则应该是,如果您发现自己在运行时处理类型,则您可能建模有误。

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