通用约束类型默认值

时间:2016-05-25 09:24:14

标签: swift

请考虑以下代码:

protocol JSONParserType {
    associatedtype Element
}

// MARK: - Entities
struct Item {}

// MARK: - Parsers
struct OuterParser<T: JSONParserType where T.Element == Item>: JSONParserType {
    typealias Element = Item
    let innerParser: T

    init(innerParser: T = InnerParser()) {
        self.innerParser = innerParser
    }
}

struct InnerParser: JSONParserType {
    typealias Element = Item
}

OuterParser有一个子解析器,应该被约束为特定类型。不幸的是,在初始化程序中(或在属性定义本身中)提供默认值会导致编译器抛出“类型为'InnerParser'的默认参数值'无法转换为类型'T'”。

如果我删除默认值赋值并仅显式提供OuterParser明确提供InnerParser,那么一切都很好。

let outerParser = OuterParser(innerParser: InnerParser())

我的问题是提供实际满足约束的默认值的方法不起作用的原因是什么。

2 个答案:

答案 0 :(得分:1)

问题是T实际类型不是由类定义的 - 它是由使用该类的代码定义的。因此,在您在类中执行任何操作(在实例或静态级别)之前,将对其进行定义。因此,您无法将InnerParser分配给T,因为T已被定义为该点的给定类型,这可能不是InnerParser

例如,让我们考虑您有另一个解析器结构:

struct AnotherParser: JSONParserType {
    typealias Element = Item
}

让我们假设您当前的代码已编译。现在考虑一下这样做会发生什么:

let parser = OuterParser<AnotherParser>()

您已将通用类型定义为AnotherParser - 但初始化工具会尝试将InnerParser分配给您的媒体资源(现在类型为AnotherParser)。这些类型不匹配,因此无法正常工作。

遵循相同的逻辑,这种实现也不会起作用:

struct OuterParser<T: JSONParserType where T.Element == Item>: JSONParserType {
    typealias Element = Item
    let innerParser: T

    init() {
        self.innerParser = InnerParser()
    }

    init(innerParser: T) {
        self.innerParser = innerParser
    }
}

由于无法保证通用类型TInnerParser的类型相同。当然,你可以强制转发到T - 但如果类型不兼容,这只会让你的代码崩溃。

不幸的是,这个问题没有真正干净的解决方案。我认为最好的选择可能是创建两个工厂方法来创建OuterParser实例。

enum Parser {
    static func createParser() -> OuterParser<InnerParser> {
        return OuterParser(innerParser:InnerParser())
    }
    static func createParser<T>(innerParser:T) -> OuterParser<T> {
        return OuterParser(innerParser:innerParser)
    }
}

let innerParser = Parser.createParser() // OuterParser<InnerParser>

let anotherParser = Parser.createParser(AnotherParser()) // OuterParser<AnotherParser>

我们在这里使用无壳枚举来避免使用额外函数污染全局命名空间。

虽然这不太快,但出于这个原因,我还建议您重新考虑如何定义解析器的逻辑。

答案 1 :(得分:0)

enter image description here

键入T更像child protocol JSONParserType您可以转换它:

init(innerParser: T = InnerParser() as! T) {
    self.innerParser = innerParser
}
相关问题