我想创建一个不限制密钥类型的Dictionary
(例如NSDictionary
)
所以我试过
var dict = Dictionary<Any, Int>()
和
var dict = Dictionary<AnyObject, Int>()
所得
error: type 'Any' does not conform to protocol 'Hashable'
var dict = Dictionary<Any, Int>()
^
<REPL>:5:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<Any, Int>()
^~~~~~~~~~~~~~~~~~~~~~
好的,我将使用Hashable
var dict = Dictionary<Hashable, Int>()
但
error: type 'Hashable' does not conform to protocol 'Equatable'
var dict = Dictionary<Hashable, Int>()
^
Swift.Equatable:2:8: note: '==' requirement refers to 'Self' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
protocol Hashable : Equatable
^
<REPL>:5:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<Hashable, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~~
所以Hashable
继承自Equatable
,但它不符合Equatable
???我不明白......
无论如何,继续尝试
typealias KeyType = protocol<Hashable, Equatable> // KeyType is both Hashable and Equatable
var dict = Dictionary<KeyType, Int>() // now you happy?
没有运气
error: type 'KeyType' does not conform to protocol 'Equatable'
var dict = Dictionary<KeyType, Int>()
^
Swift.Equatable:2:8: note: '==' requirement refers to 'Self' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type 'KeyType' does not conform to inherited protocol 'Equatable.Protocol'
protocol Hashable : Equatable
^
<REPL>:6:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<KeyType, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~
我现在迷失了,如何让编译器满意我的代码呢?
我想使用像
这样的词典var dict = Dictionary<Any, Int>()
dict[1] = 2
dict["key"] = 3
dict[SomeEnum.SomeValue] = 4
我知道我可以使用Dictionary<NSObject, Int>
,但这不是我想要的。
答案 0 :(得分:56)
您现在可以使用AnyHashable
这是一种类型删除的哈希值,专为这样的场景创建:
var dict = Dictionary<AnyHashable, Int>()
答案 1 :(得分:16)
我相信,从Swift 1.2开始,您可以使用ObjectIdentifier结构。它实现Hashable(因此Equatable)以及Comparable。您可以使用它来包装任何类实例。我猜测实现使用包装对象的hashValue底层地址,以及==运算符。
答案 2 :(得分:11)
我冒昧地在Apple Dev论坛的另一篇文章中交叉发布/链接到这个问题,这个问题是answered here。
以上链接的答案适用于6.1及更高版本:
struct AnyKey: Hashable {
private let underlying: Any
private let hashValueFunc: () -> Int
private let equalityFunc: (Any) -> Bool
init<T: Hashable>(_ key: T) {
underlying = key
// Capture the key's hashability and equatability using closures.
// The Key shares the hash of the underlying value.
hashValueFunc = { key.hashValue }
// The Key is equal to a Key of the same underlying type,
// whose underlying value is "==" to ours.
equalityFunc = {
if let other = $0 as? T {
return key == other
}
return false
}
}
var hashValue: Int { return hashValueFunc() }
}
func ==(x: AnyKey, y: AnyKey) -> Bool {
return x.equalityFunc(y.underlying)
}
答案 3 :(得分:6)
字典是struct Dictionary<Key : Hashable, Value>...
这意味着Value可以是您想要的任何内容,Key可以是您想要的任何类型,但Key必须符合Hashable
协议。
您无法创建Dictionary<Any, Int>()
或Dictionary<AnyObject, Int>()
,因为Any
和AnyObject
无法保证此类密钥符合Hashable
您无法创建Dictionary<Hashable, Int>()
,因为Hashable
不是一种类型,它只是描述所需类型的协议。
所以Hashable继承自Equatable,但它不符合 Equatable ???我不明白......
但你的术语错了。原始错误是
type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
这意味着Xcode假定'Hashable'为某种类型,但没有这种类型。并且Xcode将其视为某种类型的空类型,显然根本不符合任何协议(在这种情况下,它不符合继承的协议Equatable
)
KeyType
发生了类似的事情。
类型别名声明将现有类型的命名别名引入程序。
您看到existing type
。 protocol<Hashable, Equatable>
不是协议类型,因此Xcode再次告诉您type 'KeyType' does not conform to protocol 'Equatable'
您可以仅使用Dictionary<NSObject, Int>
,因为NSObject
符合Hashable
协议。
Swift是强类型语言,你不能做creating Dictionary that can hold anything in Key
之类的事情。实际上字典已经支持任何可以在Key中保存任何东西,它符合Hashable
。但是因为你应该指定特定的类,你不能为本机Swift类做这个,因为Swift中没有像Objective-C那样的大师类,它符合空气可以符合(借助扩展)到{{1} }
当然,你可以使用像Hashable
建议的包装器。但我真的无法想象你为什么需要它。很高兴您在Swift中有强大的输入,所以您不必像在Objective-C中那样担心类型转换
答案 4 :(得分:2)
Hashable
只是一个协议,因此您无法将其直接指定为Key
值的类型。你真正需要的是一种表达&#34;任何类型T的方式,以便T实现Hashable
。这由Swift中的类型约束处理:
func makeDict<T: Hashable>(arr: T[]) {
let x = Dictionary<T, Int>()
}
此代码编译。
AFAIK,您只能对泛型函数和类使用类型约束。
答案 5 :(得分:2)
这并没有完全回答这个问题,但对我有所帮助。
一般的答案是为所有类型实现Hashable,但是对于协议来说这可能很难,因为Hashable扩展了Equatable和Equatable使用Self,这会对协议的用途造成严重限制。
改为实现Printable,然后执行:
var dict = [String: Int]
dict[key.description] = 3
描述的实现必须是:
var description : String {
return "<TypeName>[\(<Field1>), \(<Field2>), ...]"
}
不是一个完美的答案,但我到目前为止所做的最好:(
答案 6 :(得分:0)
这不符合OP的问题,但有些相关,并且可能在某些情况下有用。假设你真正想做的是:
public var classTypeToClassNumber = [Any.Type : Int]()
但斯威夫特告诉你&#34;键入&#39; Any.Type&#39;不符合协议Hashable&#34;。
上述大部分答案都是关于将对象实例用作字典键,而不是使用对象的类型。 (这是公平的,这是OP所要求的。)正是Howard Lovatt的回答让我找到了一个可用的解决方案。
public class ClassNumberVsClassType {
public var classTypeToClassNumber = [String : Int]()
public init() {
classTypeToClassNumber[String(describing: ClassWithStringKey.self)] = 367622
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList3.self)] = 367629
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList2.self)] = 367626
classTypeToClassNumber[String(describing: ClassWithGuidKey.self)] = 367623
classTypeToClassNumber[String(describing: SimpleStruct.self)] = 367619
classTypeToClassNumber[String(describing: TestData.self)] = 367627
classTypeToClassNumber[String(describing: ETestEnum.self)] = 367617
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList0.self)] = 367624
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList1.self)] = 367625
classTypeToClassNumber[String(describing: SimpleClass.self)] = 367620
classTypeToClassNumber[String(describing: DerivedClass.self)] = 367621
}
public func findClassNumber(_ theType : Any.Type) -> Int {
var s = String(describing: theType)
if s.hasSuffix(".Type") {
s = s.substring(to: s.index(s.endIndex, offsetBy: -5)) // Remove ".Type"
}
let classNumber = _classTypeToClassNumber[s]
return classNumber != nil ? classNumber! : -1
}
}
编辑:
如果涉及的类在不同的模块中定义,并且如果忽略模块名称可能会有冲突的类名,则替换&#34; String(反映:&#34; for&#34; String(描述:& #34;,在构建字典和进行查找时都是如此。
答案 7 :(得分:-2)
您可以将类名称用作Hashable,例如:
var dict = [String: Int]
dict[object_getClassName("key")] = 3
请参阅How do I print the type or class of a variable in Swift?了解如何获取班级名称。