我有一个叫做可播放的协议,它需要func play()的实现。类Damage和DrawACard都符合协议。
protocol Playable: class {
func play(game: Game, value: Int) -> Player
}
class Damage: Playable, Hashable, Equatable {
var hashValue: Int = 1
static func ==(lhs: Damage, rhs: Damage) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func play(game: Game, value: Int) -> Player {
// do stuff
return game.activePlayer
}
}
class DrawACard: Playable, Equatable, Hashable {
var hashValue: Int = 2
static func ==(lhs: DrawACard, rhs: DrawACard) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func play(game: Game, value: Int) -> Player {
// do stuff
return game.activePlayer
}
}我的问题如下:
class Card {
var id: Int
var name: String
var cost: Int
var description: String
var target: Target
var abilities: [Playable: Int]
var cardType: CardType
init(id: Int, name: String, cost: Int, description: String, cardType: CardType, target: Target, abilities: [Playable: Int]) {
self.id = id
self.name = name
self.cost = cost
self.description = description
self.cardType = cardType
self.abilities = abilities
self.target = target
}
}类abilities中的第6个变量在我尝试将协议作为密钥时抛出一个错误。它给了我:Type 'Playable' does not conform to protocol 'Hashable'。
我希望能力变量具有实现Playable的对象的键和Int的值,例如。abilities = [DrawACard: 5]
我该怎么做呢?

发布于 2018-03-25 19:20:01
这种具有类继承的混合协议非常复杂。您的具体问题的答案是您不能直接这样做,因为如果您使Playable符合Hashable,那么您就不能创建它们的数组。
最简单的解决方案是这里不使用协议。做个抽象的课。Swift不太擅长抽象类,但它们将使这些问题大部分消失。
考虑到您的具体情况,我还可以考虑对Playable使用枚举,而不是使用协议。这可能会使事情变得更简单。
从那以后,解决方案就变得更加复杂了。例如,您可以按照我的ClassDictionary实验的思路创建一个ClassSet。或者你可以做一个类型橡皮。但事情变得很丑陋。
您还可以考虑从类切换到结构,去掉字典,只使用Array。如果Int的点是计数,那么只需拥有结构的多个副本。如果Int的点是一个参数(比如“多少损坏”),那么这应该在Damage结构中,而不是在字典中。
(不相关的注意,您的散列值非常奇怪。它们会起作用的,但哈希不是这样工作的。)
举个例子,我看到的是:
protocol Playable {
func play(game: Game) -> Player
}
struct Damage: Playable {
let amount: Int
func play(game: Game) -> Player {
// do stuff
return game.activePlayer
}
}
struct DrawACard: Playable {
func play(game: Game) -> Player {
// do stuff
return game.activePlayer
}
}
struct Card {
let id: Int
let name: String
let cost: Int
let description: String
let cardType: CardType
let target: Target
let abilities: [Playable]
}
// A card that inflicts one damage
let card = Card(id: 1,
name: "Strike",
cost: 1,
description: "Strike 'em",
cardType: CardType(),
target: Target(),
abilities: [Damage(amount: 1)])这是有意将所有内容转换为不可变的结构;我相信这里所描述的一切实际上都是值类型。
发布于 2018-03-25 19:47:56
我会将类的定义更改为此,这样类继承自一个Hashable基类:
class PlayableClass: Hashable {
var hashValue: Int = 0
static func ==(lhs: PlayableClass, rhs: PlayableClass) -> Bool {
return true
}
}
class Damage: PlayableClass, Playable {
override init() {
super.init()
hashValue = 1
}
...
}
class DrawACard: PlayableClass, Playable {
override init() {
super.init()
hashValue = 2
}
...
}
class Card {
...
var abilities: [PlayableClass: Int]
...
init(id: Int, name: String, cost: Int, description: String, abilities: [PlayableClass: Int]) {
...
}
}发布于 2020-09-07 18:00:01
这可能不会直接回答这个问题,但是,它可能会对这个问题的未来读者有所帮助。
一个想法是使用AnyHashable并使用强制转换来获取/设置所需的内容。
定义你的字典:
var abilities: [AnyHashable: Int] = [:]然后创建一些方法来添加/删除字典中的项。
func addPlayable<T: Playable>(_ myPlayable: T) where T:Hashable {
abilities[myPlayable] = 0
}
func removePlayable<T: Playable>(_ myPlayable: T) where T:Hashable {
abilities.removeValue(forKey: myPlayable)
}访问这样的元素:
func getValue<T: Playable>(_ obj: T) -> Int? where T:Hashable {
return abilities[obj]
}或者,如果您想迭代字典,可以这样做:
abilities.forEach { (it) in
(it.key as? Playable)?.play(...)
}https://stackoverflow.com/questions/49479839
复制相似问题