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

时间:2023-01-23 22:19:42

This is very hard to put into words but I've created a minimal example.

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

Here is a gist if you'd prefer... https://gist.github.com/anonymous/67d83fb2f286cf84539b58be96a971d3

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

The "data item" protocol

“数据项”协议

I have a protocol which defines Sortable objects with a property number like so.

我有一个协议,它定义了具有类似属性编号的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
    }
}

The "worker" protocol

“工人”协议

Then I have a protocol that can do something with these Sortable types. But because it has a Self requirement it needs to be defined as a protocol with an associated type and in the structs as generic property...

然后我有一个可以使用这些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}
    }
}

The handler

Finally a struct that pulls everything together...

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

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

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

        let sorter = AscendingSorter<T>()
        sortedItems = sorter.sort(items: unsortedItems)
    }
}

Making it all work

让一切顺利

This all works.

这一切都有效。

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

This prints out the array of items in the correct order depending on what type of sorter I create in the Handler

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

The problem

What I'm trying to do now is to find a property declaration for this sorter struct that can take ANY Sorter type into it but everything I have tried in doing that has failed so far.

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

Is there a way to do this?

有没有办法做到这一点?

In the struct I'd like to have...

在结构中,我想...

let sorter: SomeTypeHere

And then in the init set it like...

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

sorter = AscendingSorter()

but no combination I have tried in doing this has worked.

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

Thanks

3 个解决方案

#1


4  

You could use type erasure to implement your own AnySorter.

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

Starting with your own code from above:

从您自己的代码开始:

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]
}

Construct an AnySorter:

构建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)
    }
}

Which you make use of e.g. as an argument to the initializer in your DataHandler:

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

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

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

Your handler can now be used with a type erased AnySorter applied to your Sortable types. E.g., for the two simple sorters you've supplied in your question:

您的处理程序现在可以与应用于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, 4, 3, 2, 1]

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

Edit addition to answer your comment:

编辑附加内容以回答您的评论:

Is it possible to take the input parameter and store it in a property? Would I just use AnySorter<T> as the type of the property?

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

Yes, you can keep a property in DataHandler with type AnySorter. E.g., for a contrived example, we can let sortedItems be a computed property that makes use of an AnySorter instance to sort a stored list of items (of course in reality we don't want to do this re-sorting for each call, but for this example only!):

是的,您可以使用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, 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]

#2


3  

If an instance of a given type that conforms to Sorter can deal with any homogenous array of elements that conform to Sortable (if it's restricted to a single concrete type, then @dfri's answer has got you covered) – then Sorter need not have an associatedtype in the first place. You could simply make the sort(items:) method generic instead, which would allow you to use Sorter as a type.

如果符合Sorter的给定类型的实例可以处理符合Sortable的任何同构元素数组(如果它仅限于单个具体类型,那么@dfri的答案已经覆盖了你) - 那么Sorter不需要具有关联类型首先。你可以简单地使sort(items :)方法变得通用,这样你就可以使用Sorter作为一种类型。

Also if your sort(items:) method doesn't utilise any instance state (it doesn't in your example code), then you could make it static – and simply pass around the types of sorters, instead of instances.

此外,如果您的sort(items :)方法不使用任何实例状态(它不在您的示例代码中),那么您可以将其设置为静态 - 并简单地传递分拣机类型而不是实例。

For example, your Sortable protocol, and BasicSortable implementation:

例如,您的Sortable协议和BasicSortable实现:

protocol Sortable : Comparable {
    var number : Int { get }

    // note that you don't need to re-define the < and == operator requirements,
    // as they're already defined by Comparable and Equatable
}

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
    }
}

Your Sorter protocol, and different sorter implementations:

您的分拣机协议和不同的分拣机实施:

protocol Sorter {

    // A sort function that can take any homogenous array of a given
    // Sortable element (meaning that an instance of a type that conforms to
    // Sorter isn't restricted to a single concrete type of Sortable).
    // As the function doesn't rely on any instance state, it's static.
    static func sort<T:Sortable>(items: [T]) -> [T]
}

// Two different sorters
enum AscendingSorter : Sorter {
    static func sort<T:Sortable>(items: [T]) -> [T] {
        return items.sorted(by: <)
    }
}

enum DescendingSorter : Sorter {
    static func sort<T:Sortable>(items: [T]) -> [T] {
        return items.sorted(by: >)
    }
}

And finally, your DataHandler with an example usage:

最后,您的DataHandler示例用法:

struct DataHandler<T: Sortable> {

    let items: [T]
    private(set) var sortedItems: [T]

    var sorter : Sorter.Type { // simply hold a given type of sorter
        willSet {
            if sorter != newValue {
                // re-sort items upon (different) sorter being set
                sortedItems = newValue.sort(items: items)
            }
        }
    }

    init(unsortedItems: [T], sorter: Sorter.Type) {
        items = unsortedItems
        self.sorter = sorter
        sortedItems = sorter.sort(items: unsortedItems)
    }
}

let items = [BasicSortable(number: 2), BasicSortable(number: 4), BasicSortable(number: 6),
             BasicSortable(number: 1), BasicSortable(number: 4)]

var handler = DataHandler(unsortedItems: items, sorter: AscendingSorter.self)
print(handler.sortedItems)

// [BasicSortable(number: 1), BasicSortable(number: 2), BasicSortable(number: 4),
//  BasicSortable(number: 4), BasicSortable(number: 6)]

handler.sorter = DescendingSorter.self
print(handler.sortedItems)

// [BasicSortable(number: 6), BasicSortable(number: 4), BasicSortable(number: 4),
//  BasicSortable(number: 2), BasicSortable(number: 1)]

#3


0  

Your sorter property can't be declared as a regular Sorter because, as you pointed out, it has a Self requirement, but I believe you can do it if you add a second type argument to your DataHandler, so that it looks like

您的分拣机属性不能被声明为常规分拣机,因为正如您所指出的,它具有自我要求,但我相信如果您向DataHandler添加第二个类型参数,您可以这样做,所以它看起来像

struct DataHandler<T: Sortable, S: Sorter> {
    let items: [T]
    let sortedItems: [T]
    let sorter: S

    init(unsortedItems: [T], sorter: S) {
        items = unsortedItems

        self.sorter = sorter
        sortedItems = self.sorter.sort(items: unsortedItems)
    }
}

#1


4  

You could use type erasure to implement your own AnySorter.

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

Starting with your own code from above:

从您自己的代码开始:

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]
}

Construct an AnySorter:

构建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)
    }
}

Which you make use of e.g. as an argument to the initializer in your DataHandler:

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

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

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

Your handler can now be used with a type erased AnySorter applied to your Sortable types. E.g., for the two simple sorters you've supplied in your question:

您的处理程序现在可以与应用于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, 4, 3, 2, 1]

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

Edit addition to answer your comment:

编辑附加内容以回答您的评论:

Is it possible to take the input parameter and store it in a property? Would I just use AnySorter<T> as the type of the property?

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

Yes, you can keep a property in DataHandler with type AnySorter. E.g., for a contrived example, we can let sortedItems be a computed property that makes use of an AnySorter instance to sort a stored list of items (of course in reality we don't want to do this re-sorting for each call, but for this example only!):

是的,您可以使用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, 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]

#2


3  

If an instance of a given type that conforms to Sorter can deal with any homogenous array of elements that conform to Sortable (if it's restricted to a single concrete type, then @dfri's answer has got you covered) – then Sorter need not have an associatedtype in the first place. You could simply make the sort(items:) method generic instead, which would allow you to use Sorter as a type.

如果符合Sorter的给定类型的实例可以处理符合Sortable的任何同构元素数组(如果它仅限于单个具体类型,那么@dfri的答案已经覆盖了你) - 那么Sorter不需要具有关联类型首先。你可以简单地使sort(items :)方法变得通用,这样你就可以使用Sorter作为一种类型。

Also if your sort(items:) method doesn't utilise any instance state (it doesn't in your example code), then you could make it static – and simply pass around the types of sorters, instead of instances.

此外,如果您的sort(items :)方法不使用任何实例状态(它不在您的示例代码中),那么您可以将其设置为静态 - 并简单地传递分拣机类型而不是实例。

For example, your Sortable protocol, and BasicSortable implementation:

例如,您的Sortable协议和BasicSortable实现:

protocol Sortable : Comparable {
    var number : Int { get }

    // note that you don't need to re-define the < and == operator requirements,
    // as they're already defined by Comparable and Equatable
}

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
    }
}

Your Sorter protocol, and different sorter implementations:

您的分拣机协议和不同的分拣机实施:

protocol Sorter {

    // A sort function that can take any homogenous array of a given
    // Sortable element (meaning that an instance of a type that conforms to
    // Sorter isn't restricted to a single concrete type of Sortable).
    // As the function doesn't rely on any instance state, it's static.
    static func sort<T:Sortable>(items: [T]) -> [T]
}

// Two different sorters
enum AscendingSorter : Sorter {
    static func sort<T:Sortable>(items: [T]) -> [T] {
        return items.sorted(by: <)
    }
}

enum DescendingSorter : Sorter {
    static func sort<T:Sortable>(items: [T]) -> [T] {
        return items.sorted(by: >)
    }
}

And finally, your DataHandler with an example usage:

最后,您的DataHandler示例用法:

struct DataHandler<T: Sortable> {

    let items: [T]
    private(set) var sortedItems: [T]

    var sorter : Sorter.Type { // simply hold a given type of sorter
        willSet {
            if sorter != newValue {
                // re-sort items upon (different) sorter being set
                sortedItems = newValue.sort(items: items)
            }
        }
    }

    init(unsortedItems: [T], sorter: Sorter.Type) {
        items = unsortedItems
        self.sorter = sorter
        sortedItems = sorter.sort(items: unsortedItems)
    }
}

let items = [BasicSortable(number: 2), BasicSortable(number: 4), BasicSortable(number: 6),
             BasicSortable(number: 1), BasicSortable(number: 4)]

var handler = DataHandler(unsortedItems: items, sorter: AscendingSorter.self)
print(handler.sortedItems)

// [BasicSortable(number: 1), BasicSortable(number: 2), BasicSortable(number: 4),
//  BasicSortable(number: 4), BasicSortable(number: 6)]

handler.sorter = DescendingSorter.self
print(handler.sortedItems)

// [BasicSortable(number: 6), BasicSortable(number: 4), BasicSortable(number: 4),
//  BasicSortable(number: 2), BasicSortable(number: 1)]

#3


0  

Your sorter property can't be declared as a regular Sorter because, as you pointed out, it has a Self requirement, but I believe you can do it if you add a second type argument to your DataHandler, so that it looks like

您的分拣机属性不能被声明为常规分拣机,因为正如您所指出的,它具有自我要求,但我相信如果您向DataHandler添加第二个类型参数,您可以这样做,所以它看起来像

struct DataHandler<T: Sortable, S: Sorter> {
    let items: [T]
    let sortedItems: [T]
    let sorter: S

    init(unsortedItems: [T], sorter: S) {
        items = unsortedItems

        self.sorter = sorter
        sortedItems = self.sorter.sort(items: unsortedItems)
    }
}