2016-05-25 104 views
4

考慮下面的代碼:通用約束類型默認值

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

我的問題是什麼,該方法提供實際滿足約束不起作用的默認值的原因。

回答

1

問題是實際類型的T不是由該類定義的 - 它由使用該類的代碼定義。因此,在你做任何事情之前(無論是實例還是靜態),都要定義它。因此,您不能將InnerParser指定爲T,因爲T已被定義爲該點的給定類型,可能不是InnerParser

例如,讓我們考慮一下,你有另一個解析器結構:

struct AnotherParser: JSONParserType { 
    typealias Element = Item 
} 

,讓我們假設當前的代碼編譯。現在考慮當你這樣做會發生什麼:

let parser = OuterParser<AnotherParser>() 

你定義的一般類型爲AnotherParser - 但初始化器將嘗試(現在AnotherParser類型)分配InnerParser你的財產。這些類型不匹配,因此它不可能工作。

按照同樣的邏輯,這個實施也將無法正常工作:

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 
    } 
} 

由於沒有保證泛型類型T將相同類型InnerParser。當然,你可以強制轉換爲T - 但如果類型不兼容,那隻會讓你的代碼崩潰。

不幸的是,這個問題沒有真正的乾淨解決方案。我認爲最好的最佳選擇可能是創建兩種工廠方法來創建您的實例。

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> 

我們在這裏使用無殼枚舉,以避免污染與附加功能的全局命名空間。

雖然這不是非常Swifty,爲此我也建議可能會重新考慮如何定義解析器的邏輯。

+0

感謝您的解釋。這對我來說至少是一個棘手的問題。 – mAu

+0

@mAu高興地幫助:) – Hamish

0

enter image description here

T更像JSONParserType一個child protocol你可以把它轉換:

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