【问题标题】:Putting two generic Arrays into one Swift Dictionary with Generics使用泛型将两个泛型数组放入一个 Swift 字典
【发布时间】:2016-03-07 22:04:41
【问题描述】:

我正在尝试将两种不同的泛型类型放入一个集合中。在这个例子中,有两个数组,一个包含Ints,另一个包含Strings。

let intArray = Array<Int>()
let stringArray = Array<String>()
let dict = [1:intArray, "one": stringArray]

错误为 Type of expression is ambiguous without more context.

所以我尝试指定Dictionary的类型

let dict: [Hashable: Any] = [1:intArray, "one": stringArray]

这会导致两个错误。

  • 不支持将“Hashable”用作符合“Hashable”协议的具体类型。
  • 协议“Hashable”只能用作通用约束,因为它具有 Self 或关联类型要求

添加import Foundation 并使用NSDictionary 作为类型可以正常工作。

let dict: NSDictionary = [1:intArray, "one": stringArray]

但这在不使用 Foundation 的情况下在纯 Swift 中也应该是可能的。 Dictionary 必须是什么类型的?

edit:这显然与键的类型有关。它们必须是同一类型,而不仅仅是符合Hashable

let dict: [Int:Any] = [1:intArray, 2: stringArray]

这行得通。但是是否可以使值的类型更精确? [Int:Array&lt;Any&gt;] 不起作用。

【问题讨论】:

  • 根本原因是 Hashable 符合 Equatable,而 Equatable 有 ==(lhs: Self, rhs: Self) 要求,这就是“协议'Hashable'只能用作通用约束,因为它有 Self或相关的类型要求”。
  • 另外,您需要== 来解决哈希冲突,所以这似乎是编译器的有效投诉。
  • @AntonBronnikov 相关(但可能是重复的)链接线程涵盖了具有相同类型键的字典中的通用 values ,而这涵盖了值和键,其中后者 (通用键)是更复杂的部分。然而,我意识到这个威胁可能是 the one I linked to in the end of my answer 的副本。
  • @dfri,对不起,你是对的。 OP 的问题更广泛。

标签: ios swift macos


【解决方案1】:

详细说明来自@RobNapier 的answer,这里有一个类似的方法,它使用enum 作为字典的键和值:

enum Key: Equatable, Hashable {
    case IntKey(Int)
    case StringKey(String)

    var hashValue: Int {
        switch self {
        case .IntKey(let value)     : return 0.hashValue ^ value.hashValue
        case .StringKey(let value)  : return 1.hashValue ^ value.hashValue
        }
    }

    init(_ value: Int)    { self = .IntKey(value) }
    init(_ value: String) { self = .StringKey(value) }
}

func == (lhs: Key, rhs: Key) -> Bool {
    switch (lhs, rhs) {
    case (.IntKey(let lhsValue),    .IntKey(let rhsValue))    : return lhsValue == rhsValue
    case (.StringKey(let lhsValue), .StringKey(let rhsValue)) : return lhsValue == rhsValue
    default: return false
    }
}

enum Value {
    case IntValue(Int)
    case StringValue(String)

    init(_ value: Int)    { self = .IntValue(value) }
    init(_ value: String) { self = .StringValue(value) }
}

var dict = [Key: Value]()

dict[Key(1)] = Value("One")
dict[Key(2)] = Value(2)
dict[Key("Three")] = Value("Three")
dict[Key("Four")] = Value(4)

【讨论】:

    【解决方案2】:

    字典必须有什么类型?

    你可以试试:

    let dict: [NSObject: Any] = [1: intArray, "one": stringArray]
    

    语句let dict: [Hashable: Any] = ... 无法编译,因为Dictionary 的键的类型必须是符合Hashable具体类型,例如IntString 等。Hashable 不是具体类型。

    上述建议有效,因为 1. NSObject 是一个具体类型(您可以从中实例化对象),2. NSObjectHashable,以及 3. 因为 NSObjects 的子类实例将在这里也可以工作,并且 4. 编译器可以从字符串和数字文字初始化 NSObject 子类。

    如果您不喜欢 NSObject 作为键的类型,您可以创建自己的类或结构。

    【讨论】:

      【解决方案3】:

      请注意,如果您包含 Foundation,则您的第一次尝试 let dict = [1:intArray, "one": stringArray] 有效;产生一个NSDictionary(所以不需要明确声明这个类型)。

      在使用Foundation 时我们可以拥有这些类型的泛型字典的原因显然是编译器在将 Swift 本机类型桥接到 Foundation 时执行的隐式类型转换(在后台)。

      let intArray : [Int] = [10, 20, 30]
      let stringArray : [String] = ["foo", "baz", "bar"]
      let dict = [1:intArray, "onx": stringArray]
      
      print(dict.dynamicType)
      for e in dict {
          print(e.dynamicType, e.key.dynamicType, e.value.dynamicType)
      }
      
      /* __NSDictionaryI
         (AnyObject, AnyObject) __NSCFString _SwiftDeferredNSArray
         (AnyObject, AnyObject) __NSCFNumber _SwiftDeferredNSArray */
      

      上面dict 中的键和值都包装在AnyObject 类型中;只能保存引用(类)类型的对象; the compiler implicitly performs conversion 的值类型 Int/String 到 Foundation 引用类型 __NSCFNumber/__NSCFString。不过,这仍然是NSDictionary;例如AnyObject 本身不符合Hashable,因此不能用作原生 Swift 字典中的键。


      如果您希望创建 Swift 原生的“通用密钥”字典,我建议您创建一个符合 Hashable 的包装器(比如一个结构),并覆盖底层(各种)密钥类型(s )。参见例如(有点过时的)线程

      【讨论】:

      • 我的问题中已经提到了使用 Foundation 和 NSDictionary。
      • @orkoden 我主要解释了为什么NSDictionary 似乎在 Swift 原生类型上“通用”工作,而实际上它在后台执行到引用类型的隐式类型转换;我的答案的最后一部分(参考to this answer)指向一个现有的线程,用于使用 Swift:s native Dictionary 类型解决这个问题。 AntonBronnikov:s 的解决方案巧妙地解决了这个问题,很高兴他能帮助你!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-05
      • 1970-01-01
      相关资源
      最近更新 更多