如何创build可以容纳任何东西的字典? 或者它能够容纳的所有可能的types
我想创build一个不限制密钥types的NSDictionary
(如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
从Hashable
inheritance,但不符合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>
,但它不是我想要的。
Swift 3更新
你现在可以使用AnyHashable
这是一个types擦除哈希值,正是为这样的情景创build的:
var dict = Dictionary<AnyHashable, Int>()
我相信,从Swift 1.2开始,你可以使用一个ObjectIdentifier结构体。 它实现了Hashable(以及Equatable)以及Comparable。 你可以用它来包装任何类的实例。 我猜测实现使用包装对象的底层地址的哈希值,以及在==运算符。
我在苹果开发者论坛的另一篇post中自由地交叉发布/链接到这个问题,这个问题在这里得到了回答 。
编辑
以上链接的答案在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) }
字典是struct Dictionary<Key : Hashable, Value>...
这意味着Value可以是任何你想要的,Key可以是你想要的任何types,但Key必须符合Hashable
协议。
您不能创buildDictionary<Any, Int>()
或Dictionary<AnyObject, Int>()
,因为Any
和AnyObject
不能保证这样的Key符合Hashable
你不能创buildDictionary<Hashable, Int>()
,因为Hashable
不是一个types,它只是描述所需types的协议。
所以Hashable从Equatableinheritance,但不符合Equatable? 我不明白…
但是你的术语是错误的。 原来的错误是
type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
这意味着Xcode假定'Hashable'为某种types,但是没有这样的types。 而Xcode把它当作某种空types,显然不符合任何协议(在这种情况下它不符合inheritance协议Equatable
)
KeyType
发生类似的事情。
types别名声明将现有types的命名别名引入到程序中。
你看到existing type
。 protocol<Hashable, Equatable>
不是一个types,它是协议,所以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这样的主类,它符合(可以借助扩展)符合Hashable
当然你可以使用一些像chrisco
build议的包装。 但我真的无法想象为什么你需要它。 如果你在Swift中打字的能力很强,那么你就不用担心像Objective-C那样的types转换了
Hashable
只是一个协议,所以你不能直接指定它作为Key
值的types。 你真正需要的是一种expression“任何typesT的方式,例如T实现Hashable
。这是通过Swift中的types约束来处理的:
func makeDict<T: Hashable>(arr: T[]) { let x = Dictionary<T, Int>() }
此代码编译。
AFAIK,你只能在generics函数和类上使用types约束。
这不完全回答这个问题,但帮助了我。
一般的答案是为所有types实现Hashable,但是对于Protocols很难实现,因为Hashable扩展了Equatable和Equatable使用Self,这对协议的使用可能会造成严重的限制。
而是实现Printable,然后执行:
var dict = [String: Int] dict[key.description] = 3
描述的实现应该是这样的:
var description : String { return "<TypeName>[\(<Field1>), \(<Field2>), ...]" }
不是一个完美的答案,但迄今为止最好的:(
这并没有回答OP的问题,但有点相关,并可能希望在某些情况下使用。 假设你真正想要做的是这样的:
public var classTypeToClassNumber = [Any.Type : Int]()
但是Swift告诉你“Type”Any.Type“不符合协议Hashable”。
上面的大部分答案都是关于使用对象实例作为字典键,而不是使用对象的types。 (这是公平的,OP就是这么问的。)霍华德·洛瓦特(Howard Lovatt)的回答使我find了一个可行的解决办法。
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 } }
编辑:
如果涉及的类是在不同的模块中定义的,并且如果忽略模块名称,可能会有类名冲突,那么在构build字典时以及在执行查找时都将“String(reflect:”replace为“String 。
你可以使用类名作为Hashable,例如:
var dict = [String: Int] dict[object_getClassName("key")] = 3
请参阅如何在Swift中打印variables的types或类。 如何获得课程名称