Набор и протоколы в Swift
Я хотел бы инициализировать Set со значениями, соответствующими протоколу Hashable и пользовательскому протоколу.
Я старался:
protocol CustomProtocol: Hashable {}
let set = Set<CustomProtocol>()
Но XCode жалуется:
Использование CustomProtocol в качестве конкретного типа, соответствующего протоколу Hashable, не поддерживается
Как я могу этого достичь?
Заранее спасибо.
2 ответа
Непосредственная причина, по которой вы не можете делать то, что хотите, заключается в том, что Hashable - это общий протокол. Таким образом, он - или протокол, который вытекает из него - не может использоваться в качестве типа элемента Set. Универсальный тип может использоваться только как ограничение в другом универсальном. Вы заметите, что не можете объявить Set<Hashable>
либо, даже если тип элемента набора должен соответствовать Hashable.
Самый простой подход - создать не набор протоколов, а набор объектов определенного типа. Например, если S - это структура, соответствующая CustomProtocol (поскольку она соответствует Hashable плюс все, что влечет за собой CustomProtocol), вы можете объявить набор S.
Пример:
protocol CustomProtocol: Hashable {
}
func ==(lhs:S,rhs:S) -> Bool {
return lhs.name == rhs.name
}
struct S : CustomProtocol {
var name : String
var hashValue : Int { return name.hashValue }
}
let set = Set<S>()
Если проблема, которую вы пытаетесь решить, состоит в том, что вам нужна коллекция смешанных типов, которые, тем не менее, каким-то образом сопоставимы друг с другом, то это та же проблема, решаемая расширениями протокола, как объясняется в обсуждении в Протоколе. Ориентированное видео WWDC 2015.
Но было бы проще просто создать все классы ваших типов, производные от NSObject. Конечно, вы все равно можете заставить их принять некоторый вторичный протокол, но набор будет определяться не как набор этого протокола, а как NSObject.
В Swift 3 одним из решений является использование структуры AnyHashable.
Например, чтобы создать шаблон Observers/Observable, мы могли бы сделать:
protocol Observer {
func observableDidSomething(_ observable: Observable)
}
class Observable {
private var observersSet: Set<AnyHashable> = []
private var observers: [Observer] {
return observersSet.flatMap { $0 as? Observer }
}
func add<O>(_ observer: O) where O : Observer, O : Hashable {
observersSet.insert(observer)
}
func remove<O>(_ observer: O) where O : Observer, O : Hashable {
observersSet.remove(observer)
}
// ...
private func doSomething() {
// do something ...
observers.forEach { $0.observableDidSomething(self) }
}
}
Обратите внимание, что я отделяю Hashable
протокол из моего протокола Observer
,