2016-04-27 122 views
1

我很難讓Swift瞭解符合兩種協議的對象數組與符合其中一個的數組相同。符合多種協議的Casting類型作爲單個協議

比方說,我有兩個協議,可識別並命名的:

protocol Identifiable { 
    var identifier: Int { get } 
} 

protocol Namable { 
    var name: String { get } 
} 

這將打印符合這些協議對象的數組信息兩個功能:

func printIdentifiers(itemsToPrint: [Identifiable]) { 
    for (itemNumber, item) in itemsToPrint.enumerate() { 
     print("\(itemNumber): \(item.identifier)") 
    } 
} 

func printNames(itemsToPrint: [Namable]) { 
    for (itemNumber, item) in itemsToPrint.enumerate() { 
     print("\(itemNumber): \(item.name)") 
    } 
} 

然後兩個結構即符合這些協議:

struct Friend: Identifiable, Namable { 
    var identifier: Int 
    var name: String 
} 

struct Dog: Identifiable, Namable { 
    var identifier: Int 
    var name: String 
} 

然後說我有一個

let jeff = Friend(identifier: 232314, name: "Jeff") 
let fido = Dog(identifier: 45678, name: "Fido") 
let identifiableAndNamableItems: [protocol<Identifiable, Namable>] = [jeff, fido] 

斯威夫特有沒有問題,當我給你jeff給一個變量是Namable

let namableJeff: Namable = jeff //This is fine! 

但它怪胎的時候我嘗試做:符合這兩個協議的項目rray

printNames(identifiableAndNamableItems) 

無法將類型[protocol < Identifiable,Namable>]的值轉換爲 預期參數類型[Namable]

任何想法爲什麼? Swift直覺地知道,類型爲protocol<Identifiable, Namable>的變量可以分配給類型爲Namable的變量,因爲任何符合兩個協議的對象都必須僅符合其中一個協議。但它不明白符合兩種協議的項目數組可以分配給符合其中一種協議的項目數組。

+1

「但它不明白符合兩種協議的項目數組可以分配給符合其中一種協議的項目數組」正確,因爲「項目數組符合協議「實際上並不是一個完整的類型。請參閱我的問題在這裏的討論:http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself – matt

回答

1

夫特不能執行完全收集類型轉換(僅適用於某些後面引擎罩自動目的-C-橋接的對象,或者超類和子類元素的集合之間),其中元件集合的它們本身是相關聯的,即一個可以被分配給另一個。您需要明確地幫助編譯器顯示逐個元素的轉換是有效的,例如,使用.map操作調用printNames

printNames(identifiableAndNamableItems.map{ $0 }) 
    /* 0: Jeff 
     1: Fido */ 

還要注意在此之前,你不必全力以赴多種協議看到此行爲;同樣明顯的是例如下面更小例子

protocol Foo { } 
struct Bar: Foo {} 

let bar = Bar() 
let foo: Foo = bar // ok 

let barArr: [Bar] = [Bar(), Bar()] 
let fooArr: [Foo] = barArr // cannot convert value of type '[Bar]' to specified type '[Foo]' 
// let fooArr: [Foo] = barArr.map{ $0 } // OK 
+0

有趣。那麼這是一個很好的解決方法,而且編譯器的未來版本可能會更聰明。我已經提交了一個雷達,以防萬一,這有助於:) –

1

兩個@dfri@matt作出關於爲什麼這不起作用大點。這是隱式集合類型轉換極其有限的事實的組合,並且Swift在大多數情況下不喜歡使用非具體類型。

我唯一要補充的是,比起使用map手動轉換類型,問題稍微更具體的解決方案(雙關語意圖)是使用類型擦除,如Rob demonstrates in his answer here

這將允許你在一個新的具體類型AnyIdentifiableAndNamable(隨意提出一個更吸引人的名字)包裝你的非具體類型的protocol<Identifiable, Namable>。然後你可以使用這個具體類型來爲你的數組。

你會希望它看起來是這樣的:

struct AnyIdentifiableAndNamable:Identifiable, Namable { 

    // your non-concrete typed base 
    private let _base:protocol<Identifiable, Namable> 

    // implement protocol properties to simply return the base's property 
    var identifier: Int {return _base.identifier} 
    var name: String {return _base.name} 

    init<T:Identifiable where T:Namable>(_ base:T) { 
     _base = base 
    } 
} 

let jeff = Friend(identifier: 232314, name: "Jeff") 
let fido = Dog(identifier: 45678, name: "Fido") 
let identifiableAndNamableItems = [AnyIdentifiableAndNamable(jeff), AnyIdentifiableAndNamable(fido)] 

然後,您只需要修改您的打印功能使用泛型。例如:

func printIdentifiers<T:Identifiable>(itemsToPrint: [T]) { 
    for (itemNumber, item) in itemsToPrint.enumerate() { 
     print("\(itemNumber): \(item.identifier)") 
    } 
} 

func printNames<T:Namable>(itemsToPrint: [T]) { 
    for (itemNumber, item) in itemsToPrint.enumerate() { 
     print("\(itemNumber): \(item.name)") 
    } 
} 

現在,您不必進行任何轉換傳遞您的[AnyIdentifiableAndNamable][T],如SWIFT將推出其類型爲您服務。

+0

有趣的,謝謝一噸的解釋! –