Ответ 1
К сожалению, в настоящее время невозможно, чтобы типы метатипов соответствовали протоколам (см. Этот связанный вопрос по данному вопросу) - поэтому CellThing.Type
настоящее время не соответствует и не может соответствовать Hashable
. Следовательно, это означает, что его нельзя использовать непосредственно как Key
Dictionary
.
Однако вы можете создать оболочку для метатипа, используя ObjectIdentifier
, чтобы обеспечить реализацию Hashable
. Например:
/// Hashable wrapper for a metatype value.
struct HashableType<T> : Hashable {
static func == (lhs: HashableType, rhs: HashableType) -> Bool {
return lhs.base == rhs.base
}
let base: T.Type
init(_ base: T.Type) {
self.base = base
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(base))
}
// Pre Swift 4.2:
// var hashValue: Int { return ObjectIdentifier(base).hashValue }
}
Затем вы также можете предоставить HashableType
для Dictionary
который принимает метатип и оборачивает его в HashableType
для вас:
extension Dictionary {
subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
get { return self[HashableType(key)] }
set { self[HashableType(key)] = newValue }
}
}
который затем мог бы использовать так:
class CellThing {}
class A : CellThing {}
class B : CellThing {}
var recycle: [HashableType<CellThing>: [CellThing]] = [:]
recycle[A.self] = [A(), A(), A()]
recycle[B.self] = [B(), B()]
print(recycle[A.self]!) // [A, A, A]
print(recycle[B.self]!) // [B, B]
Это также должно хорошо работать для обобщений, вместо этого вы просто T.self
бы словарь в T.self
.
К сожалению, одним из недостатков использования нижнего индекса с get
и set
является то, что вы столкнетесь с падением производительности при работе со значениями словаря, которые являются типами копирования при записи, такими как Array
(например, в вашем примере). Я говорю об этой проблеме больше в этом Q & A.
Простая операция, как:
recycle[A.self]?.append(A())
вызовет O (N) копию массива, хранящегося в словаре.
Эта проблема предназначена для решения с помощью обобщенных средств доступа, которые были реализованы как неофициальная языковая функция в Swift 5. Если вам удобнее использовать неофициальную языковую функцию, которая может сломаться в будущей версии (не рекомендуется для производственного кода), тогда вы могли бы реализовать индекс как:
extension Dictionary {
subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
get { return self[HashableType(key)] }
_modify {
yield &self[HashableType(key)]
}
}
}
что решает проблему производительности, позволяя мутировать значение массива на месте внутри словаря.
В противном случае простой альтернативой является не определять пользовательский индекс, а вместо этого просто добавить удобное вычисляемое свойство для вашего типа, чтобы позволить вам использовать его в качестве ключа:
class CellThing {
// Convenience static computed property to get the wrapped metatype value.
static var hashable: HashableType<CellThing> { return HashableType(self) }
}
class A : CellThing {}
class B : CellThing {}
var recycle: [HashableType<CellThing>: [CellThing]] = [:]
recycle[A.hashable] = [A(), A(), A()]
recycle[B.hashable] = [B(), B()]
print(recycle[A.hashable]!) // [A, A, A]
print(recycle[B.hashable]!) // [B, B]