Swift中如何为实际可相等的非Equatable协议类型实现查找方法?
简化实现Container的indexOfItem方法方案
问题背景
现有两个Swift协议定义如下:
protocol Container { var items: [Item] {get} } protocol Item { var name: String {get} }
需要为Container扩展indexOfItem方法来查找指定Item的索引,但直接调用firstIndex(of:)会因为Item协议无法遵循Equatable而报错。已知所有实际的Item实现类都遵循Equatable,以下是两种无需类型擦除的简化实现方案。
方案一:修改Container协议,添加关联类型约束
通过给Container协议定义关联类型,并约束该类型同时遵循Item和Equatable,从根源上满足firstIndex(of:)的类型要求。
protocol Container { associatedtype ItemType: Item & Equatable var items: [ItemType] { get } } protocol Item { var name: String { get } } // 扩展实现indexOfItem extension Container { func indexOfItem(_ item: ItemType) -> Int? { items.firstIndex(of: item) } } // 示例实现类 struct Book: Item, Equatable { var name: String } struct Shelf: Container { typealias ItemType = Book var items: [Book] } // 使用示例 let shelf = Shelf(items: [Book(name: "Swift实战"), Book(name: "iOS进阶")]) let targetBook = Book(name: "iOS进阶") print(shelf.indexOfItem(targetBook)) // 输出 Optional(1)
优势:类型安全,编译器能在编译阶段严格校验类型,无需运行时类型转换。
方案二:不修改原协议,通过扩展约束或泛型实现
如果无法修改原Container协议,可以通过扩展的where子句或泛型参数,在运行时将Item转换为具体的遵循Equatable的类型进行比较。
写法1:泛型参数约束
protocol Container { var items: [Item] { get } } protocol Item { var name: String { get } } extension Container { func indexOfItem<T: Item & Equatable>(_ item: T) -> Int? { items.firstIndex { ($0 as? T) == item } } } // 示例使用 struct Book: Item, Equatable { var name: String } struct Shelf: Container { var items: [Item] = [Book(name: "Swift实战"), Book(name: "iOS进阶")] } let shelf = Shelf() let targetBook = Book(name: "iOS进阶") print(shelf.indexOfItem(targetBook)) // 输出 Optional(1)
写法2:扩展where子句约束数组元素类型
protocol Container { var items: [Item] { get } } protocol Item { var name: String { get } } extension Container where [Item].Element: Equatable { func indexOfItem(_ item: Item) -> Int? { guard let equatableItem = item as? [Item].Element else { return nil } return items.firstIndex { ($0 as? [Item].Element) == equatableItem } } } // 示例使用同写法1
优势:无需修改原有协议,适配已有代码结构;注意:依赖运行时类型转换,若传入的item类型与数组元素类型不匹配,会返回nil。
内容的提问来源于stack exchange,提问作者Vladimir
相关产品推荐
相关产品推荐

