2016-09-24 78 views
0

下面的代碼片段演示了我正在嘗試解決的問題。Swift條件一致性的解決方法(是:將約定添加到受約束的泛型類型)

import Foundation 

protocol Printable { 
    func className() -> String 
} 

class SomeType: Printable { 
    func className() -> String { 
     return "SomeType" 
    } 
} 

class List<T> { 
} 

extension List where T: SomeType { 
    func className() -> String { 
     return "List<SomeType>" 
    } 
} 

func test(type: Any, message: String) { 
    guard type is Printable else { 
     print("\(message): ERROR") 
     return 
    } 
    print("\(message): SUCCESS") 
} 

let s: Any = SomeType() 
test(type: s, message: "#1") 

let slist1: Any = List<Any>() 
test(type: slist1, message: "#2") 

let slist2: Any = List<SomeType>() 
test(type: slist2, message: "#3") 

我怎樣才能得到這樣的:

> #1: SUCCESS <--- as expected 
> #2: ERROR <--- it's okay 
> #3: SUCCESS <--- I am getting ERROR instead 

看來,增加一個協議,這條線會做的伎倆:

extension List: Printable where T: SomeType { // COMPILE ERROR 

但不幸的是,這是不允許的。

另一種方式來做到這一點可能是使用:

extension List where T: Printable { // COMPILES OK in Swift 2.3 but doesn't work. COMPILE ERROR in Swift 3.0 

但同樣,沒有運氣通過測試。

我還能做些什麼來將協議添加到約束泛型?

+1

沒有[條件一致性](https://github.com) /apple/swift/blob/master/docs/GenericsManifesto.md#conditional-conformances-)在Swift(還沒有) - 你通常可以通過重載函數來解決這個問題,例如期望一個帶有函數的Printable輸入。 'List '輸入,其中'T'可以被約束到某個協議。或者你可以創建一個包裝類型來表示一個'List '實例,其中'T'符合一些協議 - 然後你可以將這個包裝符合到'Printable'。 – Hamish

+0

如果你可以用你的建議的例子寫一個答案,這將是非常有幫助的。謝謝。 – Laurent

+0

我很抱歉沒有儘快回覆你。再看一下你的代碼,因爲你的'test'函數有一個'Any'輸入,並且你希望在運行時處理類型 - 重載對你沒有多大用處(如果你能夠保留靜態類型,那麼它會)。至於創建一個包裝類型,[這個問題和答案](http://stackoverflow.com/questions/33332613/is-it-possible-to-add-type-constraints-to-a-swift-protocol-conformance-擴展名)涵蓋了它很好(標記爲愚蠢?)。 – Hamish

回答

1

好吧所以在你的警衛你問「如果這是可打印的,然後打印成功,否則打印錯誤」,並與你的第一個例子,你有s是可打印的SomeType。沒關係。

之後,你有slist1這是類型List<Any>這是肯定不是類型打印,你會得到「錯誤」。這很好

接下來你有List<SomeType>。現在你有一個將T定義爲SomeType的類擴展,是否正確?但是你只是將T定義爲SomeType而不是實際的列表,所以當你將整個List傳遞給測試函數時,你不會讓你的測試通過,因爲List<AnyTypeHere>不是可打印的,因爲列表本身並不是' t實現可打印。

現在的問題是,你想整個列表打印?如果是這樣,那就使它符合SomeType或Printable協議。這是通過除了將各個List<SomeType>元素傳遞到函數之外的唯一途徑。你的功能邏輯是正確的,但這只是對這個概念的誤用。

所以,如果你想List<SomeType>,以使該通過,那麼你可以做這樣的事情

class List<T> : Printable where T:SomeType { 
    //Add code here that conforms to protocol 
} 

這樣做,這將使你的第二個測試失敗,因爲任何不從SOMETYPE繼承,但它會讓你的第三個測試通過,因爲現在List<T>是Printable,T也是SomeType類型。我的意思是,這只是一種真正的快速方式,可以讓你看起來像你想要的那樣。你不會同時通過第二和第三個測試,除非你添加額外的東西,因爲第二個測試是List類型爲Any,而第三個是List類型爲Printable。所以他們中的任何一個都會拋出一個錯誤(因爲List不是打印類型),或者所有的測試都顯示成功(因爲List的類型爲Printable)

+0

這很好。我想#2失敗,#3通過。所以根據你的答案加入似乎工作。爲什麼編譯器讓我編譯時沒有「」? – Laurent

+0

我的猜測會是因爲你只能擴展類型和列表不是一個類型。列表是一種類型。 List實際上是類名,而通用T就是爲你計劃處理的未知類型而設置的。但整體類型仍然是List。 編輯:這就是爲什麼當你第三次通過列表將失敗。你說T是SomeType,但整體類型列表不符合Printable,所以你會得到我剛剛嘗試的錯誤 – Eyesofbanquo

+0

,你的答案不起作用。 「擴展名列表:Printable where T:SomeType」返回「使用未聲明的類型T」。我需要類和擴展都有任何和SomeType類型的工作。如果我用最新的Swift 3刪除了,我得到了「帶有約束的類型'List'的擴展名不能有繼承子句。」所以看起來像Apple修正了允許上面的代碼編譯的漏洞。 – Laurent