代码之家  ›  专栏  ›  技术社区  ›  NaderBesada

协议字典Swift 4

  •  3
  • NaderBesada  · 技术社区  · 7 年前

    我有一个叫做playable的协议,它需要实现 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 尝试将协议作为密钥时引发错误。它给了我: Type 'Playable' does not conform to protocol 'Hashable'

    我希望abilities变量具有实现 Playable 值为 Int ,例如。 abilities = [DrawACard: 5]

    我该怎么做呢?

    Error

    3 回复  |  直到 7 年前
        1
  •  2
  •   Rob Napier    7 年前

    这种带有类继承的混合协议是出了名的复杂。你的具体问题的答案是你不能直接回答,因为如果你 Playable 符合 Hashable ,则不能对它们进行数组。

    最简单的解决方案是不要在这里使用协议。只需创建一个抽象类。Swift不太擅长抽象类,但它们可以解决大多数问题。

    考虑到您的具体情况,我还可以考虑使用枚举 可播放 而不是协议。这可能会让事情变得更简单。

    由此,解决方案变得更加复杂。例如,您可以创建 ClassDictionary 沿着我的路线 ClassSet 实验或者你可以建立一个类型橡皮擦。但它变得丑陋。

    您还可以考虑从类切换到结构,去掉字典,只使用数组。如果 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)])
    

    这有意地将一切都转换为不可变结构;我相信你在这里描述的一切都是真正的价值类型。

        2
  •  0
  •   omarzl    7 年前

    我会将类的定义更改为这样,以便类继承自可哈希基类:

    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]) {
            ...
        }
    }
    
        3
  •  0
  •   Markymark    4 年前

    这可能无法直接回答问题;然而,这可能对这个问题的未来读者有所帮助。

    一个想法是使用 AnyHashable 并使用casting获得/设置您想要的内容。
    定义词典:

    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(...)
    }