在 Smalltalk 中定义枚举类型的惯用方法是什么?
What is the idiomatic way to define an enum type in Smalltalk?
就像在 Java、C# 等中一样。我如何在 Smalltalk 中定义类似 enum Direction { input, output }
的东西?
简单的方法
最简单的方法是使用 class-side 方法 returning 符号或其他基本对象(例如整数)。
所以你可以这样写你的例子:
Direction class>>input
^ #input
Direction class>>output
^ #output
以及用法:
Direction input
主要缺点是:
- 发生在 return 相同值上的任何其他 "enum" 将等于此值,即使枚举不同(您可以 return 例如
^ self name + '::input'
)
- 在调试过程中,您会看到对象的实际值,这对于 number-based 枚举来说尤其难看(呃...这个 7 是什么意思?)
对象方法
更好的方法是创建您自己的枚举对象和它的 return 个实例。
这样的对象应该:
- 覆盖
=
和 hash
,因此您可以按您的值比较它们并使用枚举作为散列集合(字典)中的键
- 存储实际的唯一值表示
- 有自定义
printOn:
方法来简化调试
它可能看起来像这样
Object subclass: #DirectionEnum
slots: { #value }
classVariables: { }
category: 'DirectionEnum'
"enum accessors"
DirectionEnum class>>input
^ self new value: #input
DirectionEnum class>>output
^ self new value: #output
"getter/setters"
DirectionEnum>>value
^ value
DirectionEnum>>value: aValue
value := aValue
"comparing"
DirectionEnum>>= anEnum
^ self class = anEnum class and: [ self value = anEnum value ]
DirectionEnum>>hash
^ self class hash bitXor: self value hash
"printing"
DirectionEnum>>printOn: aStream
super printOn: aStream.
aStream << '(' << self value asString << ')'
用法还是一样
Direction input.
DirectionEnum output asString. "'a DirectionEnum(output)'"
并且解决了琐碎方法中提到的问题
显然这是更多的工作,但结果更好。如果你有很多枚举,那么将基本行为移动到一个新的超类 Enum
可能是有意义的,然后 DirectionEnum
只需要包含 class-side 方法。
由于 Smalltalk 是动态类型的,无论如何都不能将变量的值限制为对象的子集,因此除了通过枚举名称进行命名空间之外,枚举成员和全局常量之间没有区别。
编辑:有关如何定义枚举常量的选项,请参阅 Peter 的回答。只是让我提一下,如果足以满足您的需要,您也可以直接使用符号。 direction := #input. direction := #output
对于简单的情况,只需按照 Peter 的建议使用符号 - 您也可以将可能的值存储在 IdentityDictionary
.
中
如果您指的是 Java 中可用的更强大的枚举类型(它们不仅仅是一种命名常量;它们可以具有行为、复杂属性等),那么我会比 Peter 更进一步,只是为每个枚举类型创建一个子类。即使你在谈论大量的枚举(你的用例似乎只需要两个),也不要害怕有很多子类,其中只有一个或两个方法只是用来区分它们,大部分工作在公共超类中完成。
最接近枚举类型的 Smalltalk 特性是 SharedPool
(a.k.a。PoolDictionary
)。因此,如果您要将某些枚举从 Java 移植到 Smalltalk,您可能需要使用 SharedPool
。方法如下:
对于类型中的每个枚举,您将在池中定义一个关联,其中 key
类型名称和 value
类型值。
在某些方言中 PoolDictionaries
是词典,在 Pharo 中它们是 SharedPool
的子class。因此,在 Pharo 中,您将所有类型名称声明为 class 变量。然后在初始化方法中将值与键相关联(class 端)。
例如,您可以使用 class 变量 'Red', 'Green', 'Blue', 'Black', 'White'
等定义名为 ColorConstants
的 SharedPool
的子 class,如下所示:
SharedPool
subclass: #ColorConstants
instanceVariableNames: ''
classVariableNames: 'Red Green Blue Black White'
poolDictionaries: ''
package: 'MyPackage'
要将名称与值相关联,请在以下行添加 class 端初始化方法:
ColorConstants class >> initialize
Red := Color r: 1 g: 0 b: 0.
Green := Color r: 0 g: 1 b: 0.
Blue := Color r: 0 g: 0 b: 1.
Black := Color r: 0 g: 0 b: 0.
White := Color r: 1 g: 1 b: 1.
"and so on..."
评估 ColorConstants initialize
后,您将能够在 class
中使用池
Object
subclass: #MyClass
instanceVariableNames: 'blah'
classVariableNames: ''
poolDictionaries: 'ColorConstants'
package: 'MyPackage'
在 MyClass
(及其子classes)中,您可以按名称引用颜色:
MyClass >> displayError: aString
self display: aString foreground: Red background: White
MyClass >> displayOk: aString
self display: aString foreground: Green background: Black
就像在 Java、C# 等中一样。我如何在 Smalltalk 中定义类似 enum Direction { input, output }
的东西?
简单的方法
最简单的方法是使用 class-side 方法 returning 符号或其他基本对象(例如整数)。
所以你可以这样写你的例子:
Direction class>>input
^ #input
Direction class>>output
^ #output
以及用法:
Direction input
主要缺点是:
- 发生在 return 相同值上的任何其他 "enum" 将等于此值,即使枚举不同(您可以 return 例如
^ self name + '::input'
) - 在调试过程中,您会看到对象的实际值,这对于 number-based 枚举来说尤其难看(呃...这个 7 是什么意思?)
对象方法
更好的方法是创建您自己的枚举对象和它的 return 个实例。
这样的对象应该:
- 覆盖
=
和hash
,因此您可以按您的值比较它们并使用枚举作为散列集合(字典)中的键 - 存储实际的唯一值表示
- 有自定义
printOn:
方法来简化调试
它可能看起来像这样
Object subclass: #DirectionEnum
slots: { #value }
classVariables: { }
category: 'DirectionEnum'
"enum accessors"
DirectionEnum class>>input
^ self new value: #input
DirectionEnum class>>output
^ self new value: #output
"getter/setters"
DirectionEnum>>value
^ value
DirectionEnum>>value: aValue
value := aValue
"comparing"
DirectionEnum>>= anEnum
^ self class = anEnum class and: [ self value = anEnum value ]
DirectionEnum>>hash
^ self class hash bitXor: self value hash
"printing"
DirectionEnum>>printOn: aStream
super printOn: aStream.
aStream << '(' << self value asString << ')'
用法还是一样
Direction input.
DirectionEnum output asString. "'a DirectionEnum(output)'"
并且解决了琐碎方法中提到的问题
显然这是更多的工作,但结果更好。如果你有很多枚举,那么将基本行为移动到一个新的超类 Enum
可能是有意义的,然后 DirectionEnum
只需要包含 class-side 方法。
由于 Smalltalk 是动态类型的,无论如何都不能将变量的值限制为对象的子集,因此除了通过枚举名称进行命名空间之外,枚举成员和全局常量之间没有区别。
编辑:有关如何定义枚举常量的选项,请参阅 Peter 的回答。只是让我提一下,如果足以满足您的需要,您也可以直接使用符号。 direction := #input. direction := #output
对于简单的情况,只需按照 Peter 的建议使用符号 - 您也可以将可能的值存储在 IdentityDictionary
.
如果您指的是 Java 中可用的更强大的枚举类型(它们不仅仅是一种命名常量;它们可以具有行为、复杂属性等),那么我会比 Peter 更进一步,只是为每个枚举类型创建一个子类。即使你在谈论大量的枚举(你的用例似乎只需要两个),也不要害怕有很多子类,其中只有一个或两个方法只是用来区分它们,大部分工作在公共超类中完成。
最接近枚举类型的 Smalltalk 特性是 SharedPool
(a.k.a。PoolDictionary
)。因此,如果您要将某些枚举从 Java 移植到 Smalltalk,您可能需要使用 SharedPool
。方法如下:
对于类型中的每个枚举,您将在池中定义一个关联,其中 key
类型名称和 value
类型值。
在某些方言中 PoolDictionaries
是词典,在 Pharo 中它们是 SharedPool
的子class。因此,在 Pharo 中,您将所有类型名称声明为 class 变量。然后在初始化方法中将值与键相关联(class 端)。
例如,您可以使用 class 变量 'Red', 'Green', 'Blue', 'Black', 'White'
等定义名为 ColorConstants
的 SharedPool
的子 class,如下所示:
SharedPool
subclass: #ColorConstants
instanceVariableNames: ''
classVariableNames: 'Red Green Blue Black White'
poolDictionaries: ''
package: 'MyPackage'
要将名称与值相关联,请在以下行添加 class 端初始化方法:
ColorConstants class >> initialize
Red := Color r: 1 g: 0 b: 0.
Green := Color r: 0 g: 1 b: 0.
Blue := Color r: 0 g: 0 b: 1.
Black := Color r: 0 g: 0 b: 0.
White := Color r: 1 g: 1 b: 1.
"and so on..."
评估 ColorConstants initialize
后,您将能够在 class
Object
subclass: #MyClass
instanceVariableNames: 'blah'
classVariableNames: ''
poolDictionaries: 'ColorConstants'
package: 'MyPackage'
在 MyClass
(及其子classes)中,您可以按名称引用颜色:
MyClass >> displayError: aString
self display: aString foreground: Red background: White
MyClass >> displayOk: aString
self display: aString foreground: Green background: Black