简体   繁体   English

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

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

I'm making a Farm where everything that can be grown conforms to Growable protocol.我正在建造一个农场,所有可以种植的东西都符合 Growable 协议。 When you plant a plant, you call this func:当你种植植物时,你调用这个函数:

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

Now I want each instance of Farm to have a dictionary instance var like:现在我希望 Farm 的每个实例都有一个字典实例 var,如:

var crops = [Growable.Type: Int]

The problem is, even if I make the Growable protocol inherit Hashable, this does not help the Growable type become Hashable.问题是,即使我让 Growable 协议继承了 Hashable,这也无助于 Growable类型变成 Hashable。

In other words, even if I add an extension to Growable like this:换句话说,即使我像这样向 Growable 添加扩展:

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

... still the Growable type is not Hashable, since the Hashable protocol only concerns instances of types but not the types themselves. ...仍然 Growable类型不是 Hashable,因为 Hashable 协议只涉及类型的实例而不是类型本身。

Well, normally I would give up and say, “I am stupid, do not attempt this further.”好吧,通常我会放弃并说,“我很笨,不要再尝试了。”

However this is Swift, so I figure there must be a way to bend the language to my will, whether by making a new StaticHashable protocol and then extending the Dictionary type with a new subscript method accepting this, or by modding Swift's source code itself and then making a pitch to the Evolution list.然而,这是 Swift,所以我认为必须有一种方法可以使语言符合我的意愿,无论是通过创建一个新的 StaticHashable 协议,然后使用接受它的新下标方法扩展 Dictionary 类型,或者通过修改 Swift 的源代码本身和然后向 Evolution 列表进行宣传。

But before I go down either of those paths, I thought it wise to ask you geniuses if there is already a way to do what I want, or whether doing this is incredibly stupid and you will present me with the obviously superior approach that I was unbelievably daft to have somehow missed all along.但在我走上任何一条道路之前,我认为明智的做法是问你们天才是否已经有办法做我想做的事,或者这样做是否非常愚蠢,你们会向我展示我所采用的明显优越的方法以某种方式一直错过真是令人难以置信的愚蠢。

Note: my opinion is that Types themselves should be able to statically adhere to protocols whose funcs are not declared as static, since why should the sender of a message care whether the entity that responds is an immortal God or an ephemeral Creature, made in some God's image?注意:我的观点是,类型本身应该能够静态地遵守其 funcs 未声明为静态的协议,因为为什么消息的发送者应该关心响应的实体是不朽的上帝还是短暂的生物,在某些神的形象?

Is it possible to use a Type as a dictionary key in Swift?是否可以在 Swift 中使用 Type 作为字典键?

Well its possible , here is one way:好吧,这是可能的,这是一种方法:

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)

prints:印刷:

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

If you change the subscript definition to:如果将subscript定义更改为:

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

you can of course use any type as a key:您当然可以使用任何类型作为键:

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

prints:印刷:

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

I'll leave it up to you to decide whether this is wise , but its clearly possible .我会让你来决定这是否明智,但它显然是可能的

[Note: you cannot constrain Key to be String hence the use of the protocol LosslessStringConvertible ; [注意:您不能将Key限制为String因此使用LosslessStringConvertible协议; there might be a better choice, the Swift standard library is a moving target...]可能有更好的选择,Swift 标准库是一个移动目标...]

HTH HTH

You might consider taking a step back and review your design.您可能会考虑退后一步并检查您的设计。 You could model your Growable plants using enumerations , another powerful Swift feature.您可以使用枚举(另一个强大的 Swift 功能)为您的Growable植物建模。 For instance:例如:

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
    }
}

Using Hashable as a concrete type is not supported, as such, we need to use the type-erased AnyHashable structure instead — protocols with Self or associated types requirements can be tricky to get right!不支持使用Hashable作为具体类型,因此,我们需要使用类型擦除的AnyHashable结构来代替——具有 Self关联类型要求的协议可能很难做到!

Usage:用法:

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] [AnyHashable(Vegetable.potato): 30, AnyHashable(Vegetable.carrot): 10, AnyHashable(Vegetable.lettuce): 20]


Hashable metatypes .可散列的元类型 Regarding your original design, the correct way to express your intent would be:关于您的原始设计,表达您的意图的正确方法是:

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

ie, making the corresponding metatype Hashable as well, but extending metatypes isn't yet supported by Swift ;)即,使相应的Hashable也可Hashable ,但 Swift 尚不支持扩展元类型;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM