符合通用协议的结构类型,其关联类型也是协议

Fog*_*ter 6 generics protocols ios swift

这很难说,但我已经创建了一个最小的例子.

如果你更喜欢这里有一个要点... https://gist.github.com/anonymous/67d83fb2f286cf84539b58be96a971d3

"数据项"协议

我有一个协议,Sortable用这样的属性定义对象number.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

"工人"协议

然后我有一个可以用这些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}
    }
}
Run Code Online (Sandbox Code Playgroud)

处理程序

最后一个将所有东西拉到一起的结构......

struct DataHandler<T: Sortable> {
    let items: [T]
    let sortedItems: [T]

    init(unsortedItems: [T]) {
        items = unsortedItems

        let sorter = AscendingSorter<T>()
        sortedItems = sorter.sort(items: unsortedItems)
    }
}
Run Code Online (Sandbox Code Playgroud)

让一切顺利

这一切都有效.

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),
    BasicSortable(number: 3),
]

let handler = DataHandler(unsortedItems: array)

handler.sortedItems
Run Code Online (Sandbox Code Playgroud)

这将根据我在处理程序中创建的分拣机类型以正确的顺序打印出项目数组

问题

我现在要做的是找到这个sorter结构的属性声明,可以将任何Sorter类型放入其中,但到目前为止我尝试过的所有内容都失败了.

有没有办法做到这一点?

在结构中,我想...

let sorter: SomeTypeHere
Run Code Online (Sandbox Code Playgroud)

然后在init设置它就像...

sorter = AscendingSorter()
Run Code Online (Sandbox Code Playgroud)

但是我没有试过这样做的组合起作用了.

谢谢

dfr*_*fri 5

您可以使用类型擦除来实现您自己的AnySorter.

从上面的你自己的代码开始:

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]
}
Run Code Online (Sandbox Code Playgroud)

构建一个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)
    }
}
Run Code Online (Sandbox Code Playgroud)

您使用例如作为您的初始值设定项的参数DataHandler

struct DataHandler<T: Sortable> {
    let items: [T]
    let sortedItems: [T]

    init(unsortedItems: [T], sorter: AnySorter<T>) {
        items = unsortedItems
        sortedItems = sorter.sort(items: unsortedItems)
    }
}
Run Code Online (Sandbox Code Playgroud)

您的处理程序现在可以与AnySorter应用于您的Sortable类型的类型一起使用。例如,对于您在问题中提供的两个简单分类器:

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, 4, 3, 2, 1]

let dataHandlerAsc = DataHandler(unsortedItems: arr, sorter: AnySorter(AscendingSorter()))
print(dataHandlerAsc.sortedItems) // [1, 2, 3, 4, 8]
Run Code Online (Sandbox Code Playgroud)

编辑补充回答您的评论:

是否可以获取输入参数并将其存储在属性中?我会只是AnySorter<T>用作属性的类型吗?

是的,您可以DataHandler使用 type保留属性AnySorter。例如,对于一个人为的例子,我们可以让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, 4, 2, 8, 3]

var dataHandler = DataHandler(unsortedItems: arr, sorter: AnySorter(DescendingSorter()))
print(dataHandler.sortedItems) // [8, 4, 3, 2, 1]

dataHandler.changeSorter(newSorter: AnySorter(AscendingSorter()))
print(dataHandler.sortedItems) // [1, 2, 3, 4, 8]
Run Code Online (Sandbox Code Playgroud)