如何在不显式分配数字文字的情况下,用F#编写枚举?
我在F#中有一个枚举,如下所示:如何在不显式分配数字文字的情况下,用F#编写枚举?,f#,enums,F#,Enums,我在F#中有一个枚举,如下所示: type Creature = | SmallCreature = 0 | MediumCreature = 1 | GiantCreature = 2 | HumongousCreature = 3 | CreatureOfNondescriptSize = 4 type Creature = | SmallCreature | MediumCreature | GiantCreature | Humon
type Creature =
| SmallCreature = 0
| MediumCreature = 1
| GiantCreature = 2
| HumongousCreature = 3
| CreatureOfNondescriptSize = 4
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"
我不喜欢手动键入数字,我希望以后可以轻松地在枚举中插入更多项,而不必移动数字
我试过这个
type Creature =
| SmallCreature
| MediumCreature
| GiantCreature
| HumongousCreature
| CreatureOfNondescriptSize
但它导致了一个错误程序中稍后的类型“bioture”不是CLI枚举类型
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
let input=Int32.Parse(Console.ReadLine())
让输出=将EnumOfValue(输入)与//“Rat”匹配
|生物.媒介生物->“狗”
|生物。巨型生物->“大象”
|生物。巨大的生物->“鲸鱼”
|CreatureOfNondScriptSize->“Jon Skeet”
|——>“令人无法接受的可怕怪物”
控制台写入线(输出)
Console.WriteLine()
Console.WriteLine(“按任意键退出…”)
Console.Read()|>忽略
如何定义枚举而不为每个项目手动分配数值?很遗憾,您不能。数值重要吗?如果是这样,它在某种程度上逃避了枚举的预期用途(撇开标志不谈)。在这种情况下,你可以考虑一个阶级或歧视联盟。 事实上,你的第二个例子是一个受歧视的联盟。但是您稍后使用的
EnumOfValue
(需要一个enum)导致了错误
另一个选项是将枚举到数字的映射存储在字典中,并用字典查找替换模式匹配。然后,枚举的数值将不相关
我同意手动管理枚举值是一项繁重的工作。我希望在将来的版本中解决这个问题。正如Daniel所说,如果不指定数字等价物,就无法定义枚举。但是,您可以定义一个函数,该函数将数字转换为区分并集的对应情况:
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
这还有一个额外的好处,即模式匹配现在是完全匹配的。是否有任何方法可以获取C#中可见的
标记内部类(或案例中的标记属性)?这将使这不需要使用反射而成为可能。@Daniel-据我所知不是这样。但是,我看不出这有什么帮助:您可以使用标记值将DU转换为int,但不能将DU转换为int(除非我遗漏了什么)。从int转换为DU仍然需要一个函数,但这可以在没有反射的情况下完成,或者不依赖于大小写顺序。这不会是一个巨大的进步。我只是好奇。你知道有没有计划解决这个问题?可能是[]
属性?:-)我想这仍然会给DUs留下语法上的歧义。也许一个[]
属性可以解决这个问题,就像[]
解决接口的不确定性一样。@Daniel-我不知道是否有计划解决这个问题,但我从来没有发现它特别繁重——通常我更喜欢DUS,因为它们给了你完全匹配。虽然我不认为DUS是枚举替换中的一个掉落,但是如果你走那个路线,你可能会考虑标记你的类型,因为DU事例是封闭的命名空间的顶层成员。