如何创建可以在Key中保存任何内容的Dictionary?或它能够容纳的所有可能的类型

时间:2014-06-09 11:46:07

标签: swift

我想创建一个不限制密钥类型的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>,但这不是我想要的。

8 个答案:

答案 0 :(得分:56)

Swift 3更新

您现在可以使用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>(),因为AnyAnyObject无法保证此类密钥符合Hashable

您无法创建Dictionary<Hashable, Int>(),因为Hashable不是一种类型,它只是描述所需类型的协议。

  

所以Hashable继承自Equatable,但它不符合   Equatable ???我不明白......

但你的术语错了。原始错误是

type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol' 这意味着Xcode假定'Hashable'为某种类型,但没有这种类型。并且Xcode将其视为某种类型的空类型,显然根本不符合任何协议(在这种情况下,它不符合继承的协议Equatable

KeyType发生了类似的事情。

  

类型别名声明将现有类型的命名别名引入程序。

您看到existing typeprotocol<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?了解如何获取班级名称。