ZetCode

F# 枚举类型

最后修改于 2025 年 5 月 24 日

本文探讨了如何在 F# 中有效使用枚举类型来表示命名值的固定集合。

枚举类型(或枚举)是一种由一组命名常量组成的独立类型。枚举提供了一种为值创建有意义的符号名称的方法,使代码更具可读性和可维护性。在 F# 中,枚举是继承自 System.Enum 的值类型。

F# 支持两种创建枚举的方法:一种是使用 type 关键字配合 | 语法创建简单的枚举,另一种是使用 [<Flags>] 属性创建位标志枚举。枚举可以与它们的底层整型类型相互转换。

基本枚举定义

在 F# 中定义枚举最简单的方法是使用区分联合 (discriminated union) 语法。

main.fsx
type Color =
    | Red
    | Green
    | Blue
    | Yellow

let favorite = Color.Blue

match favorite with
| Color.Red -> printfn "Your favorite color is Red"
| Color.Green -> printfn "Your favorite color is Green"
| Color.Blue -> printfn "Your favorite color is Blue"
| Color.Yellow -> printfn "Your favorite color is Yellow"

此示例演示了基本的颜色枚举定义和模式匹配。

type Color =
    | Red
    | Green
    | Blue
    | Yellow

这定义了一个简单的枚举类型,有四个可能的值。每个值都是区分联合的一个 case。

λ dotnet fsi main.fsx
Your favorite color is Blue

冗余限定符

在 F# 中,一旦定义了一个区分联合类型,在进行模式匹配时就不需要显式引用其名称。您可以直接匹配其 case。这使得 Color 限定符在 match 表达式内部使用时是冗余的。

main.fsx
type Color =
    | Red
    | Green
    | Blue
    | Yellow

let favorite = Blue  // No need for Color.Blue

match favorite with
| Red -> printfn "Your favorite color is Red"
| Green -> printfn "Your favorite color is Green"
| Blue -> printfn "Your favorite color is Blue"
| Yellow -> printfn "Your favorite color is Yellow"

当在 Color 已被识别的同一作用域内引用一个 case 时,F# 会推断 RedGreenBlueYellow 指的是 Color 类型。因此,您可以直接使用 case,而无需在它们前面加上 Color. 前缀。

λ dotnet fsi main.fsx
Your favorite color is Blue

因此,除非存在歧义——例如,当多个区分联合具有重叠的名称时——否则显式地在 case 前加上 Color. 前缀是不必要的。这种行为在 F# 中是一致的,有助于保持代码的整洁和简洁。

带有底层值的枚举

枚举可以为每个 case 分配显式的底层值。

main.fsx
type Priority =
    | Low = 0
    | Medium = 1
    | High = 2
    | Critical = 3

let currentPriority = Priority.High

printfn "Priority value: %d" (int currentPriority)
printfn "Priority name: %A" currentPriority

此示例显示了一个为 case 分配了显式整数值的枚举。

type Priority =
    | Low = 0
    | Medium = 1
    | High = 2
    | Critical = 3

每个枚举 case 都被分配了一个特定的整数值。int 函数将枚举转换为其底层值。

λ dotnet fsi main.fsx
Priority value: 2
Priority name: High

标志枚举

F# 使用 [<Flags>] 属性支持位标志枚举。

main.fsx
open System

[<Flags>]
type Permissions =
    | None = 0
    | Read = 1
    | Write = 2
    | Execute = 4
    | All = 7

let userPermissions = Permissions.Read ||| Permissions.Write

printfn "User permissions: %A" userPermissions
printfn "Can read: %b" (userPermissions.HasFlag(Permissions.Read))
printfn "Can execute: %b" (userPermissions.HasFlag(Permissions.Execute))

此示例演示了一个用于表示权限组合的标志枚举。

[<Flags>]
type Permissions =
    | None = 0
    | Read = 1
    | Write = 2
    | Execute = 4
    | All = 7

[<Flags>] 属性启用了按位运算。值是 2 的幂,以便进行组合。

λ dotnet fsi main.fsx
User permissions: Read, Write
Can read: true
Can execute: false

枚举解析

枚举可以从字符串解析并转换为字符串。

main.fsx
type Season =
    | Spring = 0
    | Summer = 1
    | Autumn = 2
    | Winter = 3

let success, parsedSeason = System.Enum.TryParse<Season>("Summer")
let seasonString = parsedSeason.ToString()

printfn "Parse success: %b" success
printfn "Parsed value: %A" parsedSeason
printfn "String representation: %s" seasonString

此示例显示了如何在枚举和字符串之间进行转换。

let success, parsedSeason = System.Enum.TryParse<Season>("Summer")

Enum.TryParse 安全地尝试将字符串转换为枚举值。

λ dotnet fsi main.fsx
Parse success: true
Parsed value: Summer
String representation: Summer

枚举迭代

您可以迭代枚举类型的_所有_值。

main.fsx
open System

type Direction =
    | North = 0
    | East = 1
    | South = 2
    | West = 3

let allDirections = Enum.GetValues(typeof<Direction>)

printfn "All directions:"
for dir in allDirections do
    printfn "- %A" dir

此示例演示了如何获取枚举类型的_所有_值。

let allDirections = Enum.GetValues(typeof<Direction>)

Enum.GetValues 返回一个包含枚举_所有_值的数组。

λ dotnet fsi main.fsx
All directions:
- North
- East
- South
- West

带模式匹配的枚举

枚举与 F# 的模式匹配功能配合良好。

main.fsx
type TrafficLight =
    | Red
    | Yellow
    | Green

let getNextLight current =
    match current with
    | Red -> Green
    | Green -> Yellow
    | Yellow -> Red

let light = TrafficLight.Red
let nextLight = getNextLight light

printfn "Current light: %A" light
printfn "Next light: %A" nextLight

此示例显示了如何使用模式匹配与枚举。

match current with
| Red -> Green
| Green -> Yellow
| Yellow -> Red

模式匹配提供了一种处理不同枚举 case 的简洁方法。

λ dotnet fsi main.fsx
Current light: Red
Next light: Green

枚举扩展方法

您可以使用其他功能扩展枚举。

main.fsx
type CardSuit =
    | Hearts = 0
    | Diamonds = 1
    | Clubs = 2
    | Spades = 3


module CardSuitExtensions =

    type CardSuit with
        member this.Color =
            match this with
            | CardSuit.Hearts | CardSuit.Diamonds -> "Red"
            | CardSuit.Clubs | CardSuit.Spades -> "Black"
            | _ -> "Unknown"

    let suit = CardSuit.Hearts
    printfn "Suit: %A, Color: %s" suit (suit.Color)

此示例向 CardSuit 枚举添加了一个 Color 属性。

type CardSuit with
    member this.Color =
        match this with
        | CardSuit.Hearts | CardSuit.Diamonds -> "Red"
        | CardSuit.Clubs | CardSuit.Spades -> "Black"
        | _ -> "Unknown"

扩展方法允许向现有枚举类型添加功能。

λ dotnet fsi main.fsx
Suit: Hearts, Color: Red

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。