访问DU成员的命名字段

Tob*_*oly 5 f# field discriminated-union

如何访问受歧视联盟成员的指定字段?

例:

type Point = | Point of x : int * y : int
let p = Point(3, 1)
// how to access x-value or y-value of p here?
Run Code Online (Sandbox Code Playgroud)

Ant*_*fer 6

一般来说,具有命名字段的联合的工作方式与任何其他联合类型一样:您可以通过以下方式访问字段match

let x, y = 
    match p with 
    | Point (x1, y1) -> x1, y1
Run Code Online (Sandbox Code Playgroud)

F# 文档还提到了一种仅匹配某些命名参数的方法。就您而言,这意味着您可以编写:

let xOnly = 
    match p with 
    | Point (x = x1) -> x1
Run Code Online (Sandbox Code Playgroud)

如果您只有一个案例,请参阅 @pswg 的答案 该答案中的代码对于所有受歧视的联合都是通用的。对于具有命名字段的单例联合,您可以使用上面显示的特殊语法并编写:

let Point(x = myX) = p
Run Code Online (Sandbox Code Playgroud)

这将 field 的值绑定xmyX

PS根据评论:为什么你不能通过这样做立即读出字段p.x?您可以将判别联合视为一个小对象层次结构(并这样使用它,请参阅判别联合文档:“您通常可以使用判别联合作为小对象层次结构的更简单替代方案”)。考虑以下 DU:

type Shape = | Circle of r: float | Square of float
Run Code Online (Sandbox Code Playgroud)

您可以将其视为Shape超类。CircleSquare是两个派生类,每个派生类都有一个float属性。Circle您创建的或 的每个实例都Square将被向上转换为Shape.

这样,为什么不能立即读出字段就很清楚了:首先需要确定您正在查看哪个派生类,只有在转换到正确的子类之后才能读出字段。

此对象层次结构视图与 F# 内部处理 DU 的方式非常匹配:如果您查看反射中的 DU 类型,您将看到两个与联合案例同名的嵌套类型:

> typeof<Shape>.GetNestedTypes()
|> Seq.iter (fun t -> 
    let p = t.GetProperties() 
    let s = 
        p
        |> Array.map (fun p -> sprintf "%s: %s" p.Name p.PropertyType.Name) 
        |> String.concat "; "
    printfn "Nested type %s: %i Properties %s" t.Name p.Length s
);;
Nested type Tags: 0 Properties 
Nested type Circle: 4 Properties r: Double; Tag: Int32; IsCircle: Boolean; IsSquare: Boolean
Nested type Square: 4 Properties Item: Double; Tag: Int32; IsCircle: Boolean; IsSquare: Boolean
Run Code Online (Sandbox Code Playgroud)

实际数据存在于子类的属性中。对于Circle,我们使用了命名字段,您会看到属性r。对于Square,我们在属性中有数据Item(或者Item1Item2如果有多个参数)。其余的都是编译器生成的:Tag用于快速区分子类的数字字段,以及用于子类检查的两个 bool 属性。

超类本身仅具有编译器生成的属性:

>     typeof<Shape>.GetProperties()
    |> Seq.iter (fun p -> printfn "Property %s" p.Name);;
Property Tag
Property IsCircle
Property IsSquare
Run Code Online (Sandbox Code Playgroud)


p.s*_*w.g 5

对于像您的示例那样的单例区分联合,您不需要使用match-with表达式.你可以这样做:

let (Point (x, y)) = p
printf "%i" x // 3
Run Code Online (Sandbox Code Playgroud)

或者只是得到x并忽略y:

let (Point (x, _)) = p
printf "%i" x // 3
Run Code Online (Sandbox Code Playgroud)