访问 DU 成员的命名字段
Access DU member's named fields
如何访问受歧视的工会成员的命名字段?
示例:
type Point = | Point of x : int * y : int
let p = Point(3, 1)
// how to access x-value or y-value of p here?
通常,具有命名字段的联合与任何其他联合类型一样工作:您可以通过 match
:
访问这些字段
let x, y =
match p with
| Point (x1, y1) -> x1, y1
The F# documentation 也提到了一种只匹配一些命名参数的方法。在你的情况下,这意味着你可以写:
let xOnly =
match p with
| Point (x = x1) -> x1
如果您只有一个案例,请参阅@p.s.w.g 的回答。该答案中的代码适用于所有受歧视的工会。对于具有命名字段的单例联合,您可以使用上面显示的特殊语法并编写:
let Point(x = myX) = p
这将字段 x
的值绑定到 myX
。
PS 根据评论:为什么你不能通过 p.x
立即读出字段?您可以将可区分联合视为一个小的对象层次结构(并这样使用它,请参阅 discriminated union documentation: "You can often use a discriminated union as a simpler alternative to a small object hierarchy")。考虑以下 DU:
type Shape = | Circle of r: float | Square of float
您可以将 Shape
视为超类。 Circle
和Square
是两个派生的类,每个都有一个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
实际数据存在于子类的属性中。对于Circle
,我们使用命名字段,你看属性r
。对于 Square
,我们在 Item
属性 中有数据(如果有多个参数,则为 Item1
、Item2
)。其余的是编译器生成的:numeric Tag
字段将用于快速区分 sub类,以及两个用于子类检查的 bool 属性。
超类本身只有编译器生成的属性:
> typeof<Shape>.GetProperties()
|> Seq.iter (fun p -> printfn "Property %s" p.Name);;
Property Tag
Property IsCircle
Property IsSquare
对于像您的示例这样的单一案例区分联合,您不需要使用 match-with
表达式。你可以这样做:
let (Point (x, y)) = p
printf "%i" x // 3
或者只获取 x
并忽略 y
:
let (Point (x, _)) = p
printf "%i" x // 3
如何访问受歧视的工会成员的命名字段?
示例:
type Point = | Point of x : int * y : int
let p = Point(3, 1)
// how to access x-value or y-value of p here?
通常,具有命名字段的联合与任何其他联合类型一样工作:您可以通过 match
:
let x, y =
match p with
| Point (x1, y1) -> x1, y1
The F# documentation 也提到了一种只匹配一些命名参数的方法。在你的情况下,这意味着你可以写:
let xOnly =
match p with
| Point (x = x1) -> x1
如果您只有一个案例,请参阅@p.s.w.g 的回答。该答案中的代码适用于所有受歧视的工会。对于具有命名字段的单例联合,您可以使用上面显示的特殊语法并编写:
let Point(x = myX) = p
这将字段 x
的值绑定到 myX
。
PS 根据评论:为什么你不能通过 p.x
立即读出字段?您可以将可区分联合视为一个小的对象层次结构(并这样使用它,请参阅 discriminated union documentation: "You can often use a discriminated union as a simpler alternative to a small object hierarchy")。考虑以下 DU:
type Shape = | Circle of r: float | Square of float
您可以将 Shape
视为超类。 Circle
和Square
是两个派生的类,每个都有一个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
实际数据存在于子类的属性中。对于Circle
,我们使用命名字段,你看属性r
。对于 Square
,我们在 Item
属性 中有数据(如果有多个参数,则为 Item1
、Item2
)。其余的是编译器生成的:numeric Tag
字段将用于快速区分 sub类,以及两个用于子类检查的 bool 属性。
超类本身只有编译器生成的属性:
> typeof<Shape>.GetProperties()
|> Seq.iter (fun p -> printfn "Property %s" p.Name);;
Property Tag
Property IsCircle
Property IsSquare
对于像您的示例这样的单一案例区分联合,您不需要使用 match-with
表达式。你可以这样做:
let (Point (x, y)) = p
printf "%i" x // 3
或者只获取 x
并忽略 y
:
let (Point (x, _)) = p
printf "%i" x // 3