如何在没有明确指定数字文字的情况下在F#中编写枚举?

时间:2011-08-16 19:05:16

标签: f# enums

我在F#中有一个枚举,如下所示:

type Creature =
   | SmallCreature = 0
   | MediumCreature = 1
   | GiantCreature = 2
   | HumongousCreature = 3
   | CreatureOfNondescriptSize = 4

我不喜欢手动输入数字,我希望以后可以轻松地在枚举中插入更多项目而无需移动数字。

我试过这个

type Creature =
   | SmallCreature
   | MediumCreature
   | GiantCreature
   | HumongousCreature
   | CreatureOfNondescriptSize

但它在程序

中稍后导致错误The type 'Creature' is not a CLI enum type
let input = Int32.Parse(Console.ReadLine())
let output = match EnumOfValue<int, Creature>(input) with // <---Error occurs here
    | Creature.SmallCreature -> "Rat"
    | Creature.MediumCreature -> "Dog"
    | Creature.GiantCreature -> "Elephant"
    | Creature.HumongousCreature -> "Whale"
    | Creature.CreatureOfNondescriptSize -> "Jon Skeet"
    | _ -> "Unacceptably Hideous Monstrosity"

Console.WriteLine(output)
Console.WriteLine()
Console.WriteLine("Press any key to exit...")
Console.Read() |> ignore

如何在不为每个项目手动分配数值的情况下定义枚举?

2 个答案:

答案 0 :(得分:5)

不幸的是,你做不到。数值是否重要?如果是这样,它有点逃避枚举的预期用途(标记旁边)。在这种情况下,你可以考虑一个阶级或有区别的联盟。

事实上,你的第二个例子是一个受歧视的联盟。但是你后来使用期望枚举的EnumOfValue会导致错误。

另一种选择是将枚举存储到字典中的数字映射,并将模式匹配替换为字典查找。然后,枚举的数值将无关紧要。

我同意手动管理枚举值很麻烦。我希望它在未来版本中得到解决。

答案 1 :(得分:4)

正如丹尼尔所说,如果不指定数字等价物,就无法定义枚举。但是,你可以定义一个函数,它将一个数字转换为一个有区别的联合的相应情况:

open Microsoft.FSharp.Reflection

let intToDU<'t> n =
    if not (FSharpType.IsUnion typeof<'t>) then
        failwithf "%s is not a discriminated union" typeof<'t>.Name
    let cases = FSharpType.GetUnionCases(typeof<'t>)
    if n >= cases.Length || n < 0 then
        failwithf "%i is out of the range of %s's cases (0 - %i)" n typeof<'t>.Name (cases.Length - 1)
    let uc = cases.[n]
    if uc.GetFields().Length > 0 then 
        failwithf "%s.%s requires constructor arguments" typeof<'t>.Name uc.Name
    FSharpValue.MakeUnion(uc, [||]) :?> 't

然后你可以像这样使用这个通用函数:

type Creature =   
| SmallCreature   
| MediumCreature   
| GiantCreature   
| HumongousCreature   
| CreatureOfNondescriptSize

let input = int (System.Console.ReadLine())

let output = 
    match intToDU input with 
    | SmallCreature -> "Rat"    
    | Creature.MediumCreature -> "Dog"    
    | Creature.GiantCreature -> "Elephant"    
    | Creature.HumongousCreature -> "Whale"    
    | Creature.CreatureOfNondescriptSize -> "Jon Skeet"    

这样做的另一个好处是模式匹配现在是完全的。