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

时间:2016-09-13 19:44:58

标签: ios swift generics protocols

这很难说出来,但我创造了一个最小的例子。

如果你更喜欢...... 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
    }
}

“工作人员”协议

然后我有一个可以对这些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),
    BasicSortable(number: 3),
]

let handler = DataHandler(unsortedItems: array)

handler.sortedItems

根据我在Handler中创建的分拣机类型,以正确的顺序打印出项目数组

问题

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

有办法做到这一点吗?

在结构中我想要......

let sorter: SomeTypeHere

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

sorter = AscendingSorter()

但我尝试这样做没有任何组合起作用。

由于

3 个答案:

答案 0 :(得分:4)

您可以使用类型擦除来实现自己的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) } } 中初始值设定项的参数:

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] 类型一起使用。例如,您在问题中提供的两个简单分拣机:

AnySorter<T>

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

  

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

是的,您可以在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] 实例对存储的项目列表进行排序(当然,实际上我们并不想要对每个调用进行重新排序,但仅限于此示例!):

cmd := exec.Command("ls","lh")
outfile, err := os.Create("./out.txt")
if err != nil {
    panic(err)
}
defer outfile.Close()

stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
    panic(err)
}

writer := bufio.NewWriter(outfile)
defer writer.Flush()

err = cmd.Start()
if err != nil {
    panic(err)
}

go io.Copy(writer, stdoutPipe)
cmd.Wait()

答案 1 :(得分:3)

如果符合Sorter的给定类型的实例可以处理符合Sortable的任何同质元素数组(如果它仅限于单个具体类型,则{ {3}}让你了解了 - 然后Sorter首先不需要associatedtype。您可以简单地将sort(items:)方法设为通用,这样您就可以使用Sorter作为类型。

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

例如,您的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
    }
}

您的Sorter协议以及不同的分拣机实施:

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

最后,您的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)]

答案 2 :(得分:0)

您的sorter属性不能被声明为常规Sorter,因为正如您所指出的那样,它有Self要求,但我相信您可以这样做您向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)
    }
}