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 将不同的情况作为一种类型来处理。
关于运行时键入的一般注意事项:
不必在运行时处理类型。尽可能避免使用它。经验法则应该是,如果您发现自己在运行时处理类型,则您可能建模有误。
如果您不确切地知道一切如何运作,则尤其如此。
我的 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 将不同的情况作为一种类型来处理。
关于运行时键入的一般注意事项:
不必在运行时处理类型。尽可能避免使用它。经验法则应该是,如果您发现自己在运行时处理类型,则您可能建模有误。
如果您不确切地知道一切如何运作,则尤其如此。