Swift中的Equatable协议

4

我正在尝试进行简单游戏的实现。每个游戏都有一个正确答案。答案可以是整数或字符串。所以我的代码如下:

protocol Answer {}
extension Int: Answer {}
extension String: Answer {}

protocol CorrectAnswer {
    var correctAnswer: Answer { get }
}

我有一个游戏所需协议:
protocol GameDescriber {
    var name: String { get }
    var description: String { get }
    var points: Int { get }
}

以下是Game结构体的实现:

struct Game: GameDescriber, Equatable, CorrectAnswer {
    var correctAnswer: Answer
    var name: String
    var description: String
    var points: Int

    static func ==(_ lhs: Game, _ rhs:Game) -> Bool {
        if let _ = lhs.correctAnswer as? String, let _ = rhs.correctAnswer as? Int {
            return false
        }

        if let _ = lhs.correctAnswer as? Int, let _ = rhs.correctAnswer as? String {
            return false
        }

        if let lhsInt = lhs.correctAnswer as? Int, let rhsInt = rhs.correctAnswer as? Int {
            if lhsInt != rhsInt {
                return false
            }
        }

        if let lhsString = lhs.correctAnswer as? String, let rhsString = rhs.correctAnswer as? String {
            if lhsString != rhsString {
                return false
            }
        }

        return lhs.description == rhs.description &&
            lhs.name == rhs.name &&
            lhs.points == rhs.points
    }
}

如果我想添加另一种答案类型(比如 Int 数组),我需要这样做:
extension Array: Answer where Element == Int {}

但让我感到困扰的是,在实现Equatable的func ==时,我不得不考虑这个问题,以及可能出现的其他情况,这可能会很麻烦 :)

是否有解决方案,并且能否以更优雅和通用的方式实现?

1个回答

1
首先要注意,您对 == 的实现可以简化为
static func ==(_ lhs: Game, _ rhs:Game) -> Bool {
    switch (lhs.correctAnswer, rhs.correctAnswer) {
    case (let lhsInt as Int, let rhsInt as Int):
        if lhsInt != rhsInt {
            return false
        }
    case (let lhsString as String, let rhsString as String):
        if lhsString != rhsString {
            return false
        }
    default:
        return false
    }
    return lhs.description == rhs.description &&
        lhs.name == rhs.name &&
        lhs.points == rhs.points
}

这样,添加另一种答案类型只需要添加一个额外的情况。

问题在于编译器无法验证您的 == 函数中处理了所有可能的答案类型,因此这种方法容易出错。

实际上,我会使用枚举Answer而不是协议,并使其成为Equatable

enum Answer: Equatable {
    case int(Int)
    case string(String)
}

请注意,您不必实现==。自Swift 4.1起,编译器会自动合成,详见SE-0185 Synthesizing Equatable and Hashable conformance
现在,Game简化为:
struct Game: GameDescriber, Equatable, CorrectAnswer {
    var correctAnswer: Answer
    var name: String
    var description: String
    var points: Int
}

编译器还会合成==,默认实现会比较所有存储属性的相等性。
只需向枚举中添加另一种答案类型即可完成。
enum Answer: Equatable {
    case int(Int)
    case string(String)
    case intArray([Int])
}

不需要额外的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接