使用符号作为轻量级枚举
尽管 @enum 宏对于大多数用例非常有用,但在某些用例中它可能过多。@enum 的缺点包括:
- 它创建了一种新类型
- 扩展有点困难
- 它具有转换,枚举和比较等功能,在某些应用程序中可能是多余的
在需要较轻重量的替代物的情况下,可以使用 Symbol 类型。符号是实习字符串 ; 它们代表字符序列,就像字符串一样,但它们与数字唯一相关。这种独特的关联可实现快速符号相等比较
我们可能会再次实现 Card 类型,这次使用 Symbol 字段:
const ranks = Set([:ace, :two, :three, :four, :five, :six, :seven, :eight, :nine,
:ten, :jack, :queen, :king])
const suits = Set([:♣, :♦, :♥, :♠])
immutable Card
rank::Symbol
suit::Symbol
function Card(r::Symbol, s::Symbol)
r in ranks || throw(ArgumentError("invalid rank: $r"))
s in suits || throw(ArgumentError("invalid suit: $s"))
new(r, s)
end
end
我们实现内部构造函数来检查传递给构造函数的任何不正确的值。与使用 @enum 类型的示例不同,Symbols 可以包含任何字符串,因此我们必须小心我们接受哪种类型的 Symbols。这里注意使用短路条件运算符。
现在我们可以像我们期望的那样构建 Card 对象:
julia> Card(:ace, :♦)
Card(:ace,:♦)
julia> Card(:nine, :♠)
Card(:nine,:♠)
julia> Card(:eleven, :♠)
ERROR: ArgumentError: invalid rank: eleven
in Card(::Symbol, ::Symbol) at ./REPL[17]:5
julia> Card(:king, :X)
ERROR: ArgumentError: invalid suit: X
in Card(::Symbol, ::Symbol) at ./REPL[17]:6
Symbols 的一个主要好处是它们的运行时可扩展性。如果在运行时,我们希望接受(例如):eleven 作为新排名,只需运行 push!(ranks, :eleven) 就足够了。@enum 类型无法实现这种运行时可扩展性。