我有一个问题是将泛型类型的结构放在一个数组中。我知道Swift将数组的元类型转换为具体类型,这就是冲突。我试图找到一个不同的解决方案,但我认为我需要你的帮助。
这里我定义了结构和协议:
protocol ItemProtocol {
var id: String { get }
}
struct Section<T: ItemProtocol> {
var items: [T]
var renderer: Renderer<T>
}
struct Renderer<T> {
var title: (T) -> String
}
这里有两个实现ItemProtocol
的示例结构:
struct Book: ItemProtocol {
var id: String
var title: String
}
struct Car: ItemProtocol {
var id: String
var brand: String
}
这是我设置部分的方式:
let book1 = Book(id: "1", title: "Foo")
let book2 = Book(id: "2", title: "Bar")
let books = [book1, book2]
let bookSection = Section<Book>(items: books, renderer: Renderer<Book> { (book) -> String in
return "Book title: \(book.title)"
})
let car1 = Car(id: "1", brand: "Foo")
let car2 = Car(id: "2", brand: "Bar")
let cars = [car1, car2]
let carSection = Section<Car>(items: cars, renderer: Renderer<Car> { (car) -> String in
return "Car brand: \(car.brand)"
})
现在我想将这些部分放在一起。这是我试过的。但是这三行中的每一行都给我一个错误:
let sections: [Section<ItemProtocol>] = [bookSection, carSection]
let sections2: [Section] = [bookSection, carSection]
let sections3: [Section<AnyObject: ItemProtocol>] = [bookSection, carSection]
sections.forEach({ section in
section.items.forEach({ item in
let renderedTitle = section.renderer.title(item)
print("\(renderedTitle)")
})
})
对于sections
数组的声明,我收到此错误:
不支持使用'ItemProtocol'作为符合协议'ItemProtocol'的具体类型
为sections2
数组声明此错误:
无法将“Section”类型的值转换为预期的元素类型“Section”
sections3
抛出了这个:
预期'&gt;'完成通用参数列表
答案 0 :(得分:2)
结构Section
是通用的,因此您不能将其用作类型。一种解决方案可能是使用类型擦除:
创建任何ItemProtocol
包装器:
protocol ItemProtocol {
var id: String { get }
}
struct AnyItem : ItemProtocol {
private let item: ItemProtocol
init(_ item: ItemProtocol) {
self.item = item
}
// MARK: ItemProtocol
var id: String { return item.id }
}
一个类型删除部分,任何部分:
protocol SectionProtocol {
associatedtype T
var items: [T] { get }
var renderer: Renderer<T> { get }
}
struct Section<Item: ItemProtocol>: SectionProtocol {
typealias T = Item
var items: [Item]
var renderer: Renderer<Item>
var asAny: AnySection {
return AnySection(self)
}
}
struct AnySection : SectionProtocol {
typealias T = AnyItem
private let _items: () -> [T]
private let _renderer: () -> Renderer<T>
var items: [T] { return _items() }
var renderer: Renderer<T> { return _renderer() }
init<Section : SectionProtocol>(_ section: Section) {
self._items = { section.items as! [AnySection.T] }
self._renderer = { section.renderer as! Renderer<AnySection.T>}
}
}
现在你可以收集AnySection
s:
let sections: [AnySection] = [bookSection.asAny, carSection.asAny]
答案 1 :(得分:1)
问题是不同Any
类型(具有不同的通用参数)之间没有共同点(Section
除外)。一种可能的解决方案是将所有Section
类型统一到一个协议中,并使用该协议构建数组:
protocol SectionProtocol {
var genericItems: [ItemProtocol] { get }
var renderedTitles: [String] { get }
}
extension Section: SectionProtocol {
var genericItems: [ItemProtocol] { return items }
var renderedTitles: [String] {
return items.map { renderer.title($0) }
}
}
let sections: [SectionProtocol] = [bookSection, carSection]
sections.forEach { section in
section.renderedTitles.forEach { renderedTitle in
print("\(renderedTitle)")
}
}
因此,不是遍历元素,而是迭代渲染的标题,每个部分都应该能够构建。
现在这可以解决您问题中的基本用例,但是根据您使用应用中的部分,这可能还不够,您将不得不求助于类型擦除器,就像其他回答者所提到的那样。
答案 2 :(得分:0)
要使用协议实现它,您需要使用类型擦除或强制转换为Any
,并且它非常复杂或不安全。您可以采用其他路线并使用代数枚举实现它,例如:
protocol ItemProtocol: CustomStringConvertible {
var id: String { get }
}
enum ItemType {
case book(title: String)
case car(brand: String)
}
struct Item: ItemProtocol {
let id: String
let type: ItemType
var description: String {
switch self.type {
case .car(let brand):
return "Car brand: \(brand)"
case .book(let title):
return "Book title: \(title)"
}
}
}
let book1 = Item(id: "1", type: .book(title: "Title1"))
let book2 = Item(id: "2", type: .book(title: "Title2"))
let car1 = Item(id: "1", type: .car(brand: "Brand1"))
let car2 = Item(id: "2", type: .car(brand: "Brand2"))
struct Section {
let items: [Item]
}
let section1 = Section(items: [book1, book2])
let section2 = Section(items: [car1, car2])
let sections = [section1, section2]