如果你更喜欢……这是一个要点
“数据项”协议
我有一个协议,它定义了具有类似属性编号的Sortable对象.
protocol Sortable: Comparable {
var number: Int {get}
static func < (lhs:Self,rhs: Self) -> Bool
static func == (lhs:Self,rhs: Self) -> Bool
}
struct BasicSortable: Sortable {
let number: Int
static func < (lhs:BasicSortable,rhs: BasicSortable) -> Bool {
return lhs.number < rhs.number
}
static func == (lhs:BasicSortable,rhs: BasicSortable) -> Bool {
return lhs.number == rhs.number
}
}
“工人”协议
然后我有一个协议,可以使用这些Sortable类型做一些事情.但由于它具有自我要求,因此需要将其定义为具有关联类型的协议,并在结构中定义为通用属性…
protocol Sorter {
associatedtype Item: Sortable
func sort(items: [Item]) -> [Item]
}
// Two different sorters
struct AscendingSorter<T:Sortable>: Sorter {
typealias Item = T
func sort(items: [T]) -> [T] {
return items.sorted()
}
}
struct DescendingSorter<T:Sortable>: Sorter {
typealias Item = T
func sort(items: [T]) -> [T] {
return items.sorted{$0 > $1}
}
}
处理程序
最后一个将所有东西拉到一起的结构……
struct DataHandler<T: Sortable> {
let items: [T]
let sortedItems: [T]
init(unsortedItems: [T]) {
items = unsortedItems
let sorter = AscendingSorter<T>()
sortedItems = sorter.sort(items: unsortedItems)
}
}
让一切顺利
这一切都有效.
let array = [
BasicSortable(number: 1),BasicSortable(number: 8),BasicSortable(number: 13),BasicSortable(number: 3),BasicSortable(number: 4),BasicSortable(number: 14),BasicSortable(number: 5),BasicSortable(number: 12),]
let handler = DataHandler(unsortedItems: array)
handler.sortedItems
这将根据我在处理程序中创建的分拣机类型以正确的顺序打印出项目数组
问题
我现在要做的是为这个排序器结构找到一个属性声明,可以将任何Sorter类型放入其中,但到目前为止我尝试过的所有内容都失败了.
有没有办法做到这一点?
在结构中,我想…
let sorter: SomeTypeHere
然后在init设置它就像…
sorter = AscendingSorter()
但是我没有试过这样做的组合起作用了.
谢谢
解决方法
从您自己的代码开始:
protocol Sortable: Comparable {
var number: Int {get}
/* as Hamish mentions in his answer:
< and == already blueprinted in Comparable and Equatable */
}
protocol Sorter {
associatedtype Item: Sortable
func sort(items: [Item]) -> [Item]
}
构造AnySorter:
struct AnySorter<Item: Sortable>: Sorter {
private let _sort: ([Item]) -> [Item]
init<S: Sorter where S.Item == Item>(_ sorter: S) {
_sort = sorter.sort
}
func sort(items: [Item]) -> [Item] {
return _sort(items)
}
}
您使用的例如作为DataHandler中初始值设定项的参数:
struct DataHandler<T: Sortable> {
let items: [T]
let sortedItems: [T]
init(unsortedItems: [T],sorter: AnySorter<T>) {
items = unsortedItems
sortedItems = sorter.sort(items: unsortedItems)
}
}
您的处理程序现在可以与应用于Sortable类型的类型擦除AnySorter一起使用.例如,您在问题中提供的两个简单分拣机:
struct AscendingSorter<T:Sortable>: Sorter {
typealias Item = T
func sort(items: [T]) -> [T] {
return items.sorted()
}
}
struct DescendingSorter<T:Sortable>: Sorter {
typealias Item = T
func sort(items: [T]) -> [T] {
return items.sorted{$0 > $1}
}
}
/* example usage */
extension Int: Sortable {
var number: Int { return self }
}
let arr = [1,4,2,8,3]
let dataHandlerDesc = DataHandler(unsortedItems: arr,sorter: AnySorter(DescendingSorter()))
print(dataHandlerDesc.sortedItems) // [8,3,1]
let dataHandlerAsc = DataHandler(unsortedItems: arr,sorter: AnySorter(AscendingSorter()))
print(dataHandlerAsc.sortedItems) // [1,8]
编辑附加内容以回答您的评论:
Is it possible to take the input parameter and store it in a property?
Would I just useAnySorter<T>as the type of the property?
是的,您可以使用AnySorter类型在DataHandler中保留属性.例如,对于一个人为的例子,我们可以让sortedItems成为一个计算属性,利用AnySorter实例对存储的项目列表进行排序(当然,实际上我们不希望对每个调用进行重新排序,但是仅适用于此示例!):
struct DataHandler<T: Sortable> {
let items: [T]
var sortedItems: [T] { return sorter.sort(items: items) }
var sorter: AnySorter<T>
init(unsortedItems: [T],sorter: AnySorter<T>) {
items = unsortedItems
self.sorter = sorter
}
mutating func changeSorter(newSorter: AnySorter<T>) {
sorter = newSorter
}
}
/* example usage */
extension Int: Sortable {
var number: Int { return self }
}
let arr = [1,3]
var dataHandler = DataHandler(unsortedItems: arr,sorter: AnySorter(DescendingSorter()))
print(dataHandler.sortedItems) // [8,1]
dataHandler.changeSorter(newSorter: AnySorter(AscendingSorter()))
print(dataHandler.sortedItems) // [1,8]