我有一些结构符合的基本协议(模型)。它们也符合hashable
protocol Model {}
struct Contact: Model, Hashable {
var hashValue: Int { return ... }
static func ==(lhs: Contact, rhs: Contact) -> Bool { return ... }
}
struct Address: Model, Hashable {
var hashValue: Int { return ... }
static func ==(lhs: Address, rhs: Address) -> Bool { return ... }
}
我有一个函数,它接受一个符合模型([模型])的对象数组。
如何将[模型]传递给需要哈希表而不使模型可哈希的函数?
func complete(with models: [Model]) {
doSomethingWithHashable(models) //can't do this
}
func doSomethingWithHashable <T:Hashable>(_ objects: [T]) {
//
}
我尽量避免这样
protocol Model: Hashable {}
func complete<T:Model>(with models: [T]) {
runComparison(models)
}
因为当我这样做的时候,我会得到“模型不能用作通用约束…”
protocol SomethingElse {
var data: [Model] { get }
}
最佳答案
您的代码的问题在于,您所说的是Model
,它对Hashable
一致性没有任何承诺。正如您所指出的,告诉编译器有关这一点(即从Model
派生出Hashable
)的问题是,您将失去按照符合Model
的异构类型说话的能力。
如果您根本不关心Model
一致性,那么您可以使用标准库的AnyHashable
类型的擦除包装器来处理完全任意的Hashable
一致性实例。
但是,假设您确实关心Model
一致性,那么您必须为同时符合Model
和Hashable
的实例构建自己的type-erased wrapper。在my answer here中,我演示了如何为符合Equatable
的类型构建类型擦除器。这里的逻辑可以很容易地扩展为Hashable
——我们只需要存储一个额外的函数来返回实例的hashValue
。
例如:
struct AnyHashableModel : Model, Hashable {
static func ==(lhs: AnyHashableModel, rhs: AnyHashableModel) -> Bool {
// forward to both lhs's and rhs's _isEqual in order to determine equality.
// the reason that both must be called is to preserve symmetry for when a
// superclass is being compared with a subclass.
// if you know you're always working with value types, you can omit one of them.
return lhs._isEqual(rhs) || rhs._isEqual(lhs)
}
private let base: Model
private let _isEqual: (_ to: AnyHashableModel) -> Bool
private let _hashValue: () -> Int
init<T : Model>(_ base: T) where T : Hashable {
self.base = base
_isEqual = {
// attempt to cast the passed instance to the concrete type that
// AnyHashableModel was initialised with, returning the result of that
// type's == implementation, or false otherwise.
if let other = $0.base as? T {
return base == other
} else {
return false
}
}
// simply assign a closure that captures base and returns its hashValue
_hashValue = { base.hashValue }
}
var hashValue: Int { return _hashValue() }
}
然后您可以这样使用它:
func complete(with models: [AnyHashableModel]) {
doSomethingWithHashable(models)
}
func doSomethingWithHashable<T : Hashable>(_ objects: [T]) {
//
}
let models = [AnyHashableModel(Contact()), AnyHashableModel(Address())]
complete(with: models)
这里,我假设您还希望将它用作
Model
需求的包装器(假设有一些)。或者,您可以公开base
属性并从Model
本身移除AnyHashableModel
一致性,使调用者访问基础base
一致性实例的Model
struct AnyHashableModel : Hashable {
// ...
let base: Model
// ...
}
但是,您将注意到,上面的类型擦除包装器仅适用于同时为
Hashable
和Model
的类型。如果我们想讨论符合条件的实例所在的其他协议,该怎么办?一个更通用的解决方案,如我所演示的那样,是接受既
Hashable
又符合某些其他协议的类型——其类型由通用占位符表示。由于swift目前无法表示必须符合另一个通用占位符给定的协议的通用占位符;此关系必须由调用方使用
Hashable
闭包定义,以执行必要的上转换。然而,由于swift 3.1在扩展中接受了具体的同类型需求,我们可以定义一个方便的初始化器来删除这个transform
的样板文件(对于其他协议类型,这可以重复)。例如:
/// Type-erased wrapper for a type that conforms to Hashable,
/// but inherits from/conforms to a type T that doesn't necessarily require
/// Hashable conformance. In almost all cases, T should be a protocol type.
struct AnySpecificHashable<T> : Hashable {
static func ==(lhs: AnySpecificHashable, rhs: AnySpecificHashable) -> Bool {
return lhs._isEqual(rhs) || rhs._isEqual(lhs)
}
let base: T
private let _isEqual: (_ to: AnySpecificHashable) -> Bool
private let _hashValue: () -> Int
init<U : Hashable>(_ base: U, upcast: (U) -> T) {
self.base = upcast(base)
_isEqual = {
if let other = $0.base as? U {
return base == other
} else {
return false
}
}
_hashValue = { base.hashValue }
}
var hashValue: Int { return _hashValue() }
}
// extension for convenience initialiser for when T is Model.
extension AnySpecificHashable where T == Model {
init<U : Model>(_ base: U) where U : Hashable {
self.init(base, upcast: { $0 })
}
}
现在您需要将实例包装在
Model
中:func complete(with models: [AnySpecificHashable<Model>]) {
doSomethingWithHashable(models)
}
func doSomethingWithHashable<T : Hashable>(_ objects: [T]) {
//
}
let models: [AnySpecificHashable<Model>] = [
AnySpecificHashable(Contact()),
AnySpecificHashable(Address())
]
complete(with: models)
关于swift - 检查Hashable一致性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43263352/