我在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
如何在不为每个项目手动分配数值的情况下定义枚举?
答案 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"
这样做的另一个好处是模式匹配现在是完全的。