为什么我们需要动态类型语言的枚举?


32

我在这里阅读一些代码,发现一个枚举用于存储html标签的名称。为什么我们需要这样做?使用此策略有什么好处?

我知道枚举在编译或静态类型的语言中有多么有用,但是当我看到动态类型的语言中的枚举时,我会感到好奇,就像上面显示的示例代码一样。因此,问题基本上可以归结为为什么我们需要使用动态类型语言的枚举,或者根本不需要它们?


38
这个问题基本上是“我们为什么需要类型”
user11153'5

1
您是否搜索了回购以查看其名称?这样可以使您更好地理解收到的答案。
RubberDuck

如果您具有:enum People { YOU, NPC, FOO, BAR }并且需要一个(People)函数,则int可以插入任何内容,而不用使用数字。
埃文·卡斯雷克

3
您是在用任何语言询问该特定“枚举”的目的还是一般枚举的目的?我以为是前者,但目前所有的答案似乎都认为后者是……
meriton –罢工

Answers:


56

一个好处是,如果您不小心键入了“ ADRESS”或“ FEILDSET”,则编译器可以让您知道,并让您立即对其进行修复,而不是在运行时表现出荒谬的行为。

尽管好处在静态类型的语言中比在动态类型的语言中要有用得多,但即使是运行时错误,它也仍然有用,因为您将收到表示case语句而不是数据有问题的消息。


4
@amon:您可以,但是枚举为您提供了一种方便的机制来确保您不会发生冲突。
whatsisname

75
我曾经工作过的地方花了一个月的时间来追寻一个错误,该错误是由一个聪明的主意引起的,该错误是使用字符串常量而不是枚举,不良的编辑器字体以及一个意外命名的字段"PROF1LE_"
Gort the Robot

8
@amon Go正是这样做的。但是,关于枚举的一件好事是,编译器可以检查您的switch语句是否对每个可能的枚举值都有大小写。
weberc2'5

15
这段代码是在80年代编写的。您可能没有意识到这一点,但这是在一个时代,有些开发人员学会了使用物理打字机进行开发,而物理打字机可能没有一把钥匙。尽管实际上我意识到所讨论的字符串是“ Profi1e”……已经过去了25年,所以我的记忆力显然并不完美。
Gort Robot

4
@DarrelHoffman:我很惊讶你感到惊讶。在Perl中,我经常$1说我的意思是在打字$i。(当然,我从来没有键入f1le代替file的- $1错误是由事实引$1是Perl中很常见-但尽管如此,各种各样的错误是常见的也许开发了密码与prof1le在里面,从而引发他们打错profile在非密码环境中。)
ruakh

22

枚举在您拥有一组明智的固定值/实体的情况下很有用。它们是自记录的,并允许编译器验证本来可以保留给运行时的内容。如果有意义的值集未知或不受严格限制,则永远不要使用它们。

一个更有用的示例是类似HTTP响应代码的内容。您可以使用一组干净的程序集中具有有意义的名称,代码和描述等的一组枚举,而不用采用一个数字来提供错误的名称和/或描述错误的方法,而在一个干净的程序包中可以确定值是什么允许并且需要处理。


1
“自我记录”是最重要的部分。我宁愿检查是否,而status === GEOLOCATION_ACQUIRED不是status === 3,这样维护该软件的人员才能了解发生了什么。如您所说,它还可以防止无效值。
Nicolas Bouliane '16

11

枚举与OOP无关,而JavaScript没有枚举。相反,只要在一组固定的值之间进行选择,就使用枚举。例如,布尔值是在true和false之间的选择,可以将其实现为enum Bool { False, True }。在GUI库中,我们可能有一个用于对齐的枚举:enum HAlignment { LEFT = -1, CENTER = 0, RIGHT = 1 }

枚举的实现通常无关紧要,重要的是每个可能的值都是不同的。许多语言使用整数作为枚举,尽管有些语言(例如Java)支持任意对象。

到现在为止,我们也可以使用常量,例如const int LEFT = -1, CENTER = 0, RIGHT = 1。但是,编译器知道枚举值属于同一类。因此,当我切换枚举值时switch(value) {case LEFT: ...; case RIGHT: ...;},编译器会警告我我忘记了这种CENTER情况。这可以节省大量时间。在没有枚举或没有切换用例构造的语言中,可以使用“访问者模式”进行模拟,尽管这在存在静态类型的情况下更为有用。

另一个优点是可以将枚举视为单独的类型。例如,我可以声明方法采用HAlignment参数,而不是任何整数。如果我提供三个可能的HAlignment值之一,则代码将无法编译。但是,C的枚举没有很好地封装,并且枚举常量可以与整数互换使用。其他语言在这里比较严格。

在JavaScript中,我们没有获得任何这些好处。给定的示例声明一个被视为枚举的对象。这确实对程序员有一些好处,例如,它使文档编写更容易,将所有“常量”分组到一个对象中,……。但是,按照惯例,此类对象类似于枚举。

这里的重点是HTML仅具有一组有限且已知的标记。您可以查看HTML5规范,并将这些元素名称作为枚举形式放入代码中,从而使将<blink>标签潜入程序中变得更加困难。最好将这些知识编码在一个地方,以便用特殊的字符串文字(或更糟糕的是,魔术数字)来乱码您的代码。


在那种情况下,如果我<blink>在javascript中传递了某种方法,没有什么可以阻止我,对不对?但java打招呼却松懈了:)
CodeYogi's

@CodeYogi是的,因为JS没有静态类型系统,特别是没有枚举类型的概念。但是,我可以将方法参数记录为“采用TagName”,这将导致程序员调用它,foo(TagName.STRONG)因为效果会好一些。如果该字段不存在,JS会抱怨,那就更好了,但是在这里,undefined如果我尝试的话,我们只会得到一个TagName.BLINK。在JS中并没有多大价值,但这只是一个开始。
阿蒙

嗯,我上面提到的代码链接使用闭包编译器,因此在这里很有意义。
CodeYogi

Clojure是一种功能语言,因此我不确定OOP与该问题的关系如何,但除此之外,重要的是要区分通常只是一组整数(不是OO)的枚举和OO的“ Typesafe枚举”模式以及Java支持的内容。
吉米·詹姆斯(JimmyJames)

@JimmyJames我说的是JS,不是CLO Ĵ URE(其中,在Java平台上运行,还支持OOP在一定程度上)。这个问题用oop标记,这就是为什么我一开始就提到它。我没有真正看到类型安全的枚举如何连接到OOP,这只是类型系统功能(许多非OOP语言都具有类型系统:
amon

3

即使您的语言不需要编译,您也可能会使用某种IDE或开发工具,它们可以为枚举之类的东西提供更好的支持,而不仅仅是字符串。

例如,如果您在javascript中使用像对象常量这样的枚举,则编辑器将为您提供代码补全,并且如果您不小心使用了错误的值,则代码检查器(如JSHint或JSLint)将警告您。


我认为许多其他回应在这一点上都没有实现或失败。在动态语言中使用“ emem”可能不会对编译器起任何作用,但它确实可以帮助某些IDE,文档工具,并且不会忘记尝试理解代码的人员。
罗伯特

2

此类枚举的目的可能是为Js Api(goog)提供一组/捆绑的允许标签。哪个?由W3C HTML 4.01定义的代码(请参阅enum的文档)。这就是设置边界。

可能不是这个真正的目标,但是对于这样的目的,它会很好地工作。

如果您知道Javascript是如何工作的,那么代码的作用就是定义一个以字符串:-)索引的数组。哪个值是一个字符串,但可以是具有属性,函数等的任何其他组件。

除了Java脚本外,我还使用枚举来建模和管理状态机

开始> IN_PROGRESS>确认>完成> ...

在Java中,switch允许枚举,因此很容易将状态验证到状态机中,在整个枚举中循环,通过执行复杂的枚举来定义优先级,...

我还使用它们来定义类型化和不可修改的常量:

  • 是的,没有

复杂的枚举(允许进行安全的转换/解析器)

  • 是(1,true),否(0,false)

由于枚举通常属于我的模型层(核心),因此它的功能对于整个系统都是可访问的,因此它成为一个功能模型,并且我保持较低的耦合度。

枚举(除其他外)给出的是边界典型化

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.