在另一个结构中访问另一个结构中的结构值,该结构在 Swift 中是通用的
Accessing a stuct value within another stuct within another struct that is a generic in Swift
我正在尝试使用 Swift 创建纸牌游戏。我的模型是一个名为 SetGame 的结构,它包含一组类型为 struct Card 的卡片,其中 Card 中的一个变量是另一个名为 content 的结构,它是我在视图模型中设置为名为 SetCard 的结构的 CardContent 类型。它具有以下变量:颜色、阴影、numberOfShapes 和形状。
我似乎无法弄清楚如何访问模型中的 cardContent 变量,例如形状和颜色。
当我键入 print(card1.content.shape) 时,我收到一条错误消息,指出“'CardContent' 类型的值没有成员 'shape'”
当我打印 (card1) 时,我得到“Card(isSelected: true, isMatched: false, content: Set.SetCard(color: purple, numberOfShapes: 1, shape: "Diamond", shading: 0.1) , id: 54)"
当我打印 (card1.content) 时,我得到了我正在寻找的值“SetCard(color: purple, numberOfShapes: 1, shape: "Diamond", shading: 0.1)"
如何访问这些值?
struct SetGame<CardContent> where CardContent: Equatable {
private(set) var cards: Array<Card>
private(set) var dealtCards: Array<Card>
private(set) var score = 0
private(set) var selectedCards = [Int]()
init(numberOfPairOfCards: Int, creatrCardContent: (Int) -> CardContent) {
cards = []
dealtCards = [Card]()
for pairIndex in 0..<numberOfPairOfCards {
let content: CardContent = creatrCardContent(pairIndex)
cards.append(Card(content: content, id: pairIndex))
}
cards.shuffle()
for _ in 0..<12{
if let dealtCard = cards.popLast(){
self.dealtCards.append(dealtCard)
}
}
}
mutating func choose(_ card: Card){
if let chosenIndex = dealtCards.firstIndex(where: {[=11=].id == card.id}) {
if !dealtCards[chosenIndex].isSelected {
selectedCards.append(dealtCards[chosenIndex].id)
} else {
selectedCards.removeLast()
}
dealtCards[chosenIndex].isSelected = !dealtCards[chosenIndex].isSelected
print(selectedCards)
}
if selectedCards.count == 3 {
print("Let's see if they match")
print(doesMatch(cards: selectedCards))
}
}
func doesMatch(cards: [Int]) -> Bool {
// Get card content from id in cards array
if let card1 = dealtCards.first(where: { [=11=].id == cards[0]}) {
print(card1)
print(card1.content)
print(card1.content.shape)
}
return false
}
struct Card: Identifiable {
var isSelected = false
var isMatched = false
let content: CardContent
let id : Int
}
}
struct SetCard : Equatable {
let color : Color
let numberOfShapes: Int
let shape : String
let shading : Double
}
class SetGameViewModel: ObservableObject {
typealias Card = SetGame<SetCard>.Card
@Published private var model: SetGame<SetCard>
init() {
self.model = SetGameViewModel.createGame()
}
var cards: Array<Card> {
return model.cards
}
var dealtCards: Array<Card> {
return model.dealtCards
}
static func createGame() -> SetGame<SetCard> {
var cardDeck : [SetCard] = []
let shapes = ["Diamond", "Squiggle", "Oval"]
let colors = [Color.red, Color.green, Color.purple]
let counts = [1,2,3]
let shades = [0.1, 0.5, 1]
for color in colors {
for shape in shapes {
for count in counts {
for shade in shades {
let card = SetCard.init(color: color, numberOfShapes: count, shape: shape, shading: shade)
cardDeck.append(card)
}
}
}
}
return SetGame<SetCard>(numberOfPairOfCards: cardDeck.count) {pairIndex in
cardDeck[pairIndex]
}
}
// Mark:= Intents(s)
func choose(_ card: Card){
model.choose(card)
}
}
您将 content
设为泛型,所以它的类型实际上是 Equatable
。这意味着您需要将 content
转换为其实际类型 SetCard
,现在您可以访问其属性:
if let card1 = dealtCards.first(where: { [=10=].id == cards[0]}) {
print(card1)
print(card1.content)
let content = card1.content as! SetCard
print(content.shape)
}
您已将 SetGame
卡片内容通用化,因此您可能不希望您的游戏逻辑 依赖于 ,或者假设那些卡片上有什么,对吧?游戏应该适用于任何类型的卡片内容。
但是,在您的 doesMatch
方法中,您正试图通过访问卡片内容的 shape
属性 来完全做到这一点。您假设该游戏使用的任何卡片内容都有 shape
属性。如果我使用我刚刚制作的随机 Foo
结构作为卡片内容会怎么样?
struct Foo: Equatable {
let x: Int
}
Foo
中没有shape
属性,是吗?这就是出现错误的原因。
从方法的名称 doesMatch
来看,您实际上不需要访问卡片的形状或类似的东西。为什么不直接比较卡片内容的相等性,看它们是否匹配?毕竟,您已将它们限制为 Equatable
。
if let card1 = dealtCards.first(where: { [=11=].id == cards[0]}),
let card2 = dealtCards.first(where: { [=11=].id == cards[1]}) {
let firstTwoCardsMatch = card1.content == card2.content
}
如果你有更复杂的匹配逻辑,你应该写一个Matchable
这样的协议:
protocol Matchable {
func doesMatch(other: Self) -> Bool
}
struct SetGame<CardContent> where CardContent: Equatable & Matchable {
...
// in doesMatch, you can do:
let firstTwoCardsMatch = card1.content.doesMatch(other: card2.content)
}
// write your matching logic in SetCard
struct SetCard : Equatable, Matchable {
let color : Color
let numberOfShapes: Int
let shape : String
let shading : Double
func doesMatch(other: SetCard) -> Bool {
...
}
}
如果你出于某种原因非常想访问那里的 shape
属性,你可以引入一个新的协议,它有一个 shape
属性:
protocol CardContentWithShape {
var shape: String { get }
}
然后符合SetCard
:
struct SetCard : Equatable, CardContentWithShape {
并将CardContent
约束到它:
struct SetGame<CardContent> where CardContent: Equatable & CardContentWithShape {
现在 CardContent
只能是 Equatable
和 CardContentWithShape
的东西,所以它必须有一个 shape
属性,这样你就可以做到:
print(card1.content.shape)
我正在尝试使用 Swift 创建纸牌游戏。我的模型是一个名为 SetGame 的结构,它包含一组类型为 struct Card 的卡片,其中 Card 中的一个变量是另一个名为 content 的结构,它是我在视图模型中设置为名为 SetCard 的结构的 CardContent 类型。它具有以下变量:颜色、阴影、numberOfShapes 和形状。
我似乎无法弄清楚如何访问模型中的 cardContent 变量,例如形状和颜色。
当我键入 print(card1.content.shape) 时,我收到一条错误消息,指出“'CardContent' 类型的值没有成员 'shape'”
当我打印 (card1) 时,我得到“Card(isSelected: true, isMatched: false, content: Set.SetCard(color: purple, numberOfShapes: 1, shape: "Diamond", shading: 0.1) , id: 54)"
当我打印 (card1.content) 时,我得到了我正在寻找的值“SetCard(color: purple, numberOfShapes: 1, shape: "Diamond", shading: 0.1)"
如何访问这些值?
struct SetGame<CardContent> where CardContent: Equatable {
private(set) var cards: Array<Card>
private(set) var dealtCards: Array<Card>
private(set) var score = 0
private(set) var selectedCards = [Int]()
init(numberOfPairOfCards: Int, creatrCardContent: (Int) -> CardContent) {
cards = []
dealtCards = [Card]()
for pairIndex in 0..<numberOfPairOfCards {
let content: CardContent = creatrCardContent(pairIndex)
cards.append(Card(content: content, id: pairIndex))
}
cards.shuffle()
for _ in 0..<12{
if let dealtCard = cards.popLast(){
self.dealtCards.append(dealtCard)
}
}
}
mutating func choose(_ card: Card){
if let chosenIndex = dealtCards.firstIndex(where: {[=11=].id == card.id}) {
if !dealtCards[chosenIndex].isSelected {
selectedCards.append(dealtCards[chosenIndex].id)
} else {
selectedCards.removeLast()
}
dealtCards[chosenIndex].isSelected = !dealtCards[chosenIndex].isSelected
print(selectedCards)
}
if selectedCards.count == 3 {
print("Let's see if they match")
print(doesMatch(cards: selectedCards))
}
}
func doesMatch(cards: [Int]) -> Bool {
// Get card content from id in cards array
if let card1 = dealtCards.first(where: { [=11=].id == cards[0]}) {
print(card1)
print(card1.content)
print(card1.content.shape)
}
return false
}
struct Card: Identifiable {
var isSelected = false
var isMatched = false
let content: CardContent
let id : Int
}
}
struct SetCard : Equatable {
let color : Color
let numberOfShapes: Int
let shape : String
let shading : Double
}
class SetGameViewModel: ObservableObject {
typealias Card = SetGame<SetCard>.Card
@Published private var model: SetGame<SetCard>
init() {
self.model = SetGameViewModel.createGame()
}
var cards: Array<Card> {
return model.cards
}
var dealtCards: Array<Card> {
return model.dealtCards
}
static func createGame() -> SetGame<SetCard> {
var cardDeck : [SetCard] = []
let shapes = ["Diamond", "Squiggle", "Oval"]
let colors = [Color.red, Color.green, Color.purple]
let counts = [1,2,3]
let shades = [0.1, 0.5, 1]
for color in colors {
for shape in shapes {
for count in counts {
for shade in shades {
let card = SetCard.init(color: color, numberOfShapes: count, shape: shape, shading: shade)
cardDeck.append(card)
}
}
}
}
return SetGame<SetCard>(numberOfPairOfCards: cardDeck.count) {pairIndex in
cardDeck[pairIndex]
}
}
// Mark:= Intents(s)
func choose(_ card: Card){
model.choose(card)
}
}
您将 content
设为泛型,所以它的类型实际上是 Equatable
。这意味着您需要将 content
转换为其实际类型 SetCard
,现在您可以访问其属性:
if let card1 = dealtCards.first(where: { [=10=].id == cards[0]}) {
print(card1)
print(card1.content)
let content = card1.content as! SetCard
print(content.shape)
}
您已将 SetGame
卡片内容通用化,因此您可能不希望您的游戏逻辑 依赖于 ,或者假设那些卡片上有什么,对吧?游戏应该适用于任何类型的卡片内容。
但是,在您的 doesMatch
方法中,您正试图通过访问卡片内容的 shape
属性 来完全做到这一点。您假设该游戏使用的任何卡片内容都有 shape
属性。如果我使用我刚刚制作的随机 Foo
结构作为卡片内容会怎么样?
struct Foo: Equatable {
let x: Int
}
Foo
中没有shape
属性,是吗?这就是出现错误的原因。
从方法的名称 doesMatch
来看,您实际上不需要访问卡片的形状或类似的东西。为什么不直接比较卡片内容的相等性,看它们是否匹配?毕竟,您已将它们限制为 Equatable
。
if let card1 = dealtCards.first(where: { [=11=].id == cards[0]}),
let card2 = dealtCards.first(where: { [=11=].id == cards[1]}) {
let firstTwoCardsMatch = card1.content == card2.content
}
如果你有更复杂的匹配逻辑,你应该写一个Matchable
这样的协议:
protocol Matchable {
func doesMatch(other: Self) -> Bool
}
struct SetGame<CardContent> where CardContent: Equatable & Matchable {
...
// in doesMatch, you can do:
let firstTwoCardsMatch = card1.content.doesMatch(other: card2.content)
}
// write your matching logic in SetCard
struct SetCard : Equatable, Matchable {
let color : Color
let numberOfShapes: Int
let shape : String
let shading : Double
func doesMatch(other: SetCard) -> Bool {
...
}
}
如果你出于某种原因非常想访问那里的 shape
属性,你可以引入一个新的协议,它有一个 shape
属性:
protocol CardContentWithShape {
var shape: String { get }
}
然后符合SetCard
:
struct SetCard : Equatable, CardContentWithShape {
并将CardContent
约束到它:
struct SetGame<CardContent> where CardContent: Equatable & CardContentWithShape {
现在 CardContent
只能是 Equatable
和 CardContentWithShape
的东西,所以它必须有一个 shape
属性,这样你就可以做到:
print(card1.content.shape)