是否可以在 Swift 中使用类型作为字典键?

Is it possible to use a Type as a dictionary key in Swift?

我正在建造一个农场,所有可以种植的东西都符合 Growable 协议。当你种植植物时,你调用这个函数:

myFarm.planting<T: Growable>(qty: Int, of: T.Type) -> Farm

现在我希望 Farm 的每个实例都有一个字典实例变量,例如:

var crops = [Growable.Type: Int]

问题是,即使我让 Growable 协议继承 Hashable,这也无助于 Growable type 变成 Hashable。

换句话说,即使我像这样向 Growable 添加扩展:

extension Growable {
    static func hashValue {
        // return some hash
    }
}

... Growable type 仍然不是 Hashable,因为 Hashable 协议只涉及 类型的实例 而不是类型本身.

嗯,通常我会放弃并说:“我很笨,不要再尝试了。”

但是这是 Swift,所以我认为必须有一种方法可以让语言屈从于我的意愿,无论是通过制定新的 StaticHashable 协议,然后使用接受此协议的新下标方法扩展 Dictionary 类型,或者通过修改 Swift 的源代码本身,然后向 Evolution 列表推销。

但在我走上任何一条道路之前,我认为明智的做法是问问你们这些天才是否已经有办法做我想做的事情,或者这样做是否非常愚蠢,你们会向我展示明显的我一直以来都以某种方式错过了一种令人难以置信的愚蠢方法。

注意:我的观点是类型本身应该能够静态地遵守其功能未声明为静态的协议,因为消息的发送者为什么要关心响应的实体是不朽的上帝还是短暂的生物,以某种上帝的形象创造?

您可以考虑退后一步,重新审视您的设计。您可以使用另一个强大的 Swift 功能 枚举 为您的 Growable 植物建模。例如:

protocol Growable {
    /* ... */
}

enum Vegetable: String, Hashable, Growable {
    case carrot, lettuce, potato /* ... */
}

enum Mushroom: String, Hashable, Growable {
    /* ... */
}

struct Farm {
    var crops = [AnyHashable: Int]()
    mutating func plant<T: Growable & Hashable>(qty: Int, of growable: T) {
        crops[growable] = qty
    }
}

不支持使用 Hashable 作为具体类型,因此,我们需要使用 type-erased AnyHashable 结构来代替——协议自我 关联类型要求可能很难做到正确!

用法:

var myFarm = Farm()
myFarm.plant(qty: 10, of: Vegetable.carrot)
myFarm.plant(qty: 20, of: Vegetable.lettuce)
myFarm.plant(qty: 30, of: Vegetable.potato)

print(myFarm.crops)

[AnyHashable(Vegetable.potato): 30, AnyHashable(Vegetable.carrot): 10, AnyHashable(Vegetable.lettuce): 20]


可哈希元类型。关于您的原始设计,表达您的意图的正确方式是:

extension Growable.Type: Hashable {
    /* make this meta thing hashable! */
}

即,也制作相应的 元类型 Hashable,但 Swift 尚不支持扩展元类型 ;)

Is it possible to use a Type as a dictionary key in Swift?

可能,这是一种方法:

protocol Growable { ... }

struct S : Growable { ... }
class C : Growable { ... }

extension Dictionary where Key : LosslessStringConvertible
{
   subscript(index: Growable.Type) -> Value?
   {
      get
      {
         return self[String(describing: index) as! Key]
      }
      set(newValue)
      {
         self[String(describing: index) as! Key] = newValue
      }
   }
}

var d : [String:Int] = [:]
d[S.self] = 42
d[C.self] = 24
print(d)

打印:

["C": 24, "S": 42]

如果将 subscript 定义更改为:

subscript(index: Any.Type) -> Value?

您当然可以使用任何类型作为键:

var d : [String:Int] = [:]
d[S.self] = 42
d[C.self] = 24
d[type(of:d)] = 18
print(d)

打印:

["C": 24, "S": 42, "Dictionary<String, Int>": 18]

这是否明智,由您来决定,但显然可能

[注意:您不能将 Key 约束为 String,因此使用协议 LosslessStringConvertible;可能有更好的选择,Swift 标准库是一个移动的目标...]

HTH