2017-08-11 139 views
2

我的問題可能很簡單,但它讓我困惑了一下: 想象一下,我有一組不同的對象,它們都有一個共同的父類「MyClass」。 Swift的泛型和子類

var values = [MyClass]() 

但是它們都有其特定的子類,例如像「MyClassSubclass」。

values.append(MyClassSubclass()) 

我現在要創建一個通用的方法返回我這個數組是MyClassSubclass類型內的第一個對象。我想阻止對象的轉換,但是有一個泛型方法,它將子類對象作爲T參數,並返回此數組中第一個出現的子類T.

我認爲是這樣的(但肯定不工作):

func getFirst<T: MyClass>(_ ofType : T.Type) -> T? 

我想我只是卡住了,我不知道該怎麼尋找,因此,如果有人可以幫助我我將不勝感激。

編輯例如基於上述值:

class MyClass {} 
class MyClassSubclass : MyClass {} 
class MyClassSubclass2 : MyClass{} 

var values = [MyClass]() 
values.append(MyClassSubclass()) 
values.append(MyClassSubclass2()) 

//Return the first class element appearing here as a subclass type 
func getFirst<T>(_ ofType : T.Type) -> T?{} 

感謝

回答

5

一種方法是迭代陣列之上並使用可選的結合 ,以檢查是否一個元素是給定類型的:

func getFirst<T: MyClass>(ofType: T.Type) -> T? { 
    for elem in values { 
     if let item = elem as? T { 
      return item 
     } 
    } 
    return nil 
} 

這可以使用for caseas圖案被簡化:

func getFirst<T: MyClass>(ofType: T.Type) -> T? { 
    for case let item as T in values { 
     return item 
    } 
    return nil 
} 

另一種方法是使用flatMap查找給定的 類型的所有項並返回第一個:

func getFirst<T: MyClass>(ofType: T.Type) -> T? { 
    return values.flatMap { $0 as? T }.first 
} 

如果陣列可能很大,並且要避免 中間陣列的創建,那麼你可以使用lazy

func getFirst<T: MyClass>(ofType: T.Type) -> T? { 
    return values.lazy.flatMap { $0 as? T }.first 
} 

爲陣列擴展方法,這將是

extension Array { 
    func getFirst<T>(ofType: T.Type) -> T? { 
     return flatMap { $0 as? T }.first 
    } 
} 
+0

我喜歡你的方法。你認爲哪一個會是第一次返回最快的? – binloan

+0

@binloan:我的猜測是for循環是最快的,需要測量和比較。但我會建議先檢查這部分的性能是否對您的應用至關重要。如果數組不大,那麼我懷疑會有很大的差異。 –

+0

好吧,我會測試一下。我的數組目前並不大,但這可能會在將來發生,我希望有一個適當的實現。 – binloan

1

這感覺有點像仿製藥的濫用,但這裏的一個擴展:

extension Array where Element == MyClass { 
    func getFirst<T>(_ ofType: T.Type) -> T? { 
     return self.first(where: { ofType == type(of: $0) }) as? T 
    } 
} 

的方法可以被稱爲let first = values.getFirst(MyClassSubclass.self)

我個人更喜歡簡單的鑄造直列爲清楚:

let first = values.first(where: { type(of: $0) == MyClassSubclass.self }) as? MyClassSubclass 
+0

然而,我喜歡擴展的方式來澄清這一點。它將成爲觸摸框架的一部分,框架用戶應該能夠輕鬆地檢索對象類型的第一個實例。這就是爲什麼我想要這個。所以擴展會很糟糕,因爲需要的公共範圍。另外你的第二種方法將無法正常工作,因爲它將負載移動到框架用戶(我是對嗎?) - – binloan

+0

爲什麼擴展在框架中不好?很多框架使用內置的Swift和UIKit類型的擴展。 – xoudini

1

如果您想使用全局函數,這是不推薦的,試試這個

func getFirst<T>(ofType: T.Type) -> T? where T: MyClass { 
    for value in values where value is T { 
    return value as? T 
    } 
    return nil 
} 

let first = getFirst(ofType: MyClassSubclass2.self) 

第一個答案應該對Swift更好。

+0

這看起來相當順利,即使「?」與使用「if let」結構相比,投射會更慢。但爲什麼你認爲這個功能不被推薦? – binloan

+0

主要是因爲它不適合重複使用,所以「擴展數組」更好 – user5685969