Swift 4 泛型数组作为泛型中的参数class

Swift 4 generic array as parameter in generic class

我有一个用于测试的匹配器

class GWMatcher<ResultType> {
    let result: ResultType
    let message: String

    init(result: ResultType, message: String) {
        self.result = result
        self.message = message
    }
}

而且我想添加扩展以将其与数组一起使用。我希望它是这样的:

extension GWMatcher where ResultType == [Equatable] {
    func checkEqual(_ expression: ResultType) {
        XCTAssertEqual(self.result, expression, self.message)
    }
}

但是编译器说:

Cannot invoke XCTAssertEqual with an argument list of type ([Equatable], Array<Equatable>, String).

有没有人有想法,是否可以做这样的事情?

我相信您遇到了 Swift 类型系统的边缘情况。特别是,通用约束:

extension GWMatcher where ResultType == [Equatable]

确实应该指定为:

extension GWMatcher where ResultType == [T] where T: Equatable

但目前不支持最后一种形式(对于 extensions)。这很重要,因为 Swift 协议确实 符合自身,这需要符合 [Equatable] 按预期工作(这就是为什么具体类型 T 是必需的)。我知道,非常棘手... ;)

此外,标准 Equatable protocol can only be used as a generic constraint because it has Self 要求(使用 关联类型 的协议也有类似的行为)。这个 Swift 限制也可能是这里的罪魁祸首。

解决方法。无论如何,作为 less 类型安全的解决方法,试试这个:

extension GWMatcher {
    func checkEqual<T: Equatable>(_ expression: [T]) {
        guard let result = self.result as? [T] else {
            XCTFail("Expected type \([T].self)")
            return
        }
        XCTAssertEqual(result, expression)
    }
}

这不像您的原始设计那样类型安全,因为它将在 所有 GWMatcher 类型上可用,包括那些 ResultType 不是未输入 [Equatable]。尽管如此,它应该会按预期工作。