即使键入基础值,我的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"
此示例的输出是“通过反射匹配”,由于框值具有不同的CLR类型-Result.Ok,因此ResultA永远不会匹配。由于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,因为装箱的值是不同的CLR类型-Result.Ok
那不是原因。与类型匹配与C#中的
is
/ as
运算符完全相同-即,它可以匹配子类型以及确切的类型。 DU成员被编译为DU类型本身的子类型。这就是F#如何使.NET以一种类型处理不同情况的方式。关于在运行时键入的注释,通常为:
不必在运行时处理类型。尽可能避免它。经验法则应该是,如果您发现自己在运行时处理类型,则可能是建模错误。
如果您不完全了解所有工作原理,则尤其如此。