通常创建枚举来支持特殊的类或者结构体的功能。相似的,很方便的定义单纯为了在非常复杂的类的上下文中使用的工具类或者结构体。为了实现这个,Swift允许定义内嵌类型,把支持的枚举,类,和结构体内嵌在他们支持的类型定义中。
要在另一个类型中内嵌一个类型,在它支持的类型的外花括号中写它的定义。类型可以需要多少内嵌多少层。
内嵌类型的使用(Nested Types in Action)
下面的例子定义了一个名为BlackjackCard的结构体,模型化一个在Blackjack游戏中用的卡牌游戏。BlackjackCard结构体包含两个内嵌枚举,名为Suit和Rank。
在Blackjack,Ace卡牌有一个值1或者11。这个特性用一个名为Values的结构体表示,内嵌在Rank没居中:
struct BlackjackCard {
// nested Suit enumeration
enum Suit: Character {
case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
}
// nested Rank enumeration
enum Rank: Int {
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .ace:
return Values(first: 1, second: 11)
case .jack, .queen, .king:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}
// BlackjackCard properties and methods
let rank: Rank, suit: Suit
var description: String {
var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)"
if let second = rank.values.second {
output += " or \(second)"
}
return output
}
}Suit枚举描述了四个普通的卡牌花色,有一个Character的RawValue来表示他们的符号。
Rank枚举描述了13个可能的扑克牌等级,用一个Int的Raw value来表示他们的面值。(这个Int的Raw value不用于Jack,Queen,King,和Ace卡牌。)
像上面提到的,Rank枚举定义了一个自己的深一层的内嵌结构体,名为Values。这个结构体封装了大多数卡片有一个值的事实,但是Ace卡片有两个值。Values结构体定义了两个属性来表示这个:
- first,Int类型
- second,Int?类型,或者“optional Int”
Rank也定义了一个计算属性,values,返回Values结构体的实例。这个计算属性考虑卡牌的rank并且在它的rank基础上用合适的值初始化一个新的Values实例。为jack,queen,king,和ace用特殊的值。对于数字卡片,使用rank的Int 类型的Raw value。
BlackjackCard结构体自己有两个属性--rank和suit。也定义了一个名为description的计算属性,使用rank和suit中的存储属性来创建关于卡片的name和value的描述。description属性使用可选绑定来检查是否有第二个值来展示,如果这样,给第二个值插入额外的描述细节。
因为BlackjackCard是一个没有自定义初始化器的结构体,他有一个隐式的成员初始化器,像在Memberwise Initializers for Structure Types中描述的。可以使用这个初始化器来初始化一个名为theAceOfSpades的新的常量:
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// Prints "theAceOfSpades: suit is ♠, value is 1 or 11"即使Rank和Suit是内嵌在BlackjackCard中,他们的类型可以从上下文中推导出来,所以这个实力的初始化可以只通过他们的case名字(.ace和.speades)来引用枚举的cases。在上面的例子中,description属性正确的表示了黑桃Ace有一个1的值或者11的值。
引用内嵌函数(Referring to Nested Types)
为了在它定义的上下文之外使用内嵌类型,它的名字之前用它内嵌的类型的名字:
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
// heartsSymbol is "♡"上面的例子,这使得Suit、Rank和Values的名称可以故意保持简短,因为它们的名称自然会受到定义它们的上下文的限制。