使用枚举的默认值作为字典键而不显式转换为 String
Using an enum's default value as a dictionary key without explicitly casting to String
如果我这样声明 enum
:
enum Keys {
case key_one
case key_two
}
我可以打印它会自动转换成String
:
print(Keys.key_one) // prints "key_one"
如果我然后制作一个将 Strings
映射到任何内容的字典(但为了简单起见,让我们再次选择 Strings
),我认为它应该能够通过使用 [=18 添加一个键=] 作为关键,对吧?错了。
var myDict = [String : String]()
/// error: cannot subscript a value of type '[String : String]' with an index
/// of type 'Keys'
myDict[Keys.key_one] = "firstKey"
如果我明确地将 Keys.key_one
转换为 String
,我可以做到这一点:
myDict[String(Keys.key_one)] = "firstKey"
print(myDict) // ["key_one": "firstKey"]
所以我想这样做而不必每次都用 String()
包装我的 enum
。
我尝试了一些事情,通过使用 where
关键字和 Key
从 Dictionary
中执行 extension
并尝试实现新的 subscript
功能,但我无法让它工作。有什么诀窍?
解决方案更新
我找到了我的解决方案 ()。
您的键是字符串有多重要?如果它不重要,我建议做这样的事情:
enum Keys {
case key_one
case key_two
}
var myDict = [Keys : String]()
myDict[Keys.key_one] = "firstKey"
print(myDict) // [Keys.key_one: "firstKey"]
或者你可以这样做:
enum Keys: String {
case key_one
case key_two
}
并像这样使用它:
var myDict = [String : String]()
myDict[Keys.key_one.rawValue] = "firstKey"
更新
如果你真的想让它工作,你可以按照下面的方法做。但这些都是强制展开的一些风险。
extension Dictionary where Key: StringLiteralConvertible {
subscript (key: Keys) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
然后你像这样使用它:
myDict[Keys.key_one] = "firstKey"
myDict[Keys.key_one]
Swift 4.2
的更新和改进
extension Dictionary {
subscript(key: APIKeys) -> Value? {
get {
guard let key = key.stringValue as? Key else { return nil }
return self[key]
}
set(value) {
guard let key = key.stringValue as? Key else { return }
guard let value = value else { self.removeValue(forKey: key); return }
self.updateValue(value, forKey: key)
}
}
}
protocol APIKeys {}
extension APIKeys {
var stringValue: String {
return String(describing: self)
}
}
enum Keys: APIKeys {
case key_one
case key_two
}
var myStringDict = [AnyHashable : Any]()
var model1StringDict = [String : Any]()
var model2StringDict = [String : String]()
myStringDict.updateValue("firstValue", forKey: Keys.key_one.stringValue) // [key_one: firstValue]
myStringDict[Keys.key_two] = "secondValue" // [key_two: secondValue, key_one: firstValue]
myStringDict[Keys.key_one] = nil // [key_two: secondValue]
myStringDict.removeValue(forKey: Keys.key_two.stringValue) // []
model1StringDict.updateValue("firstValue", forKey: Model1Keys.model_1_key_one.stringValue) // [model_1_key_one: firstValue]
model1StringDict[Model1Keys.model_1_key_two] = "secondValue" // [model_1_key_two: secondValue, model_1_key_one: firstValue]
model1StringDict[Model1Keys.model_1_key_one] = nil // [model_1_key_two: secondValue]
model2StringDict.updateValue("firstValue", forKey: Model2Keys.model_2_key_one.stringValue) // [model_2_key_one: firstValue]
model2StringDict[Model2Keys.model_2_key_two] = "secondValue" // [model_2_key_two: secondValue, model_2_key_one: firstValue]
model2StringDict[Model2Keys.model_2_key_one] = nil // [model_2_key_two: secondValue]
我专门更改了 3 个词典的类型以显示键入词典的常用方式([AnyHashable : Any]
、[String : Any]
和 [String : String]
),并表明这适用于每种类型。
需要注意的是,当你的字典的键是AnyHashable
时,如果你使用updateValue
而不是赋值运算符,那么你需要指定String
值用 .stringValue
键。否则,存储的密钥的确切类型将不会明确地是 String
,如果您尝试通过将 nil
分配给删除密钥下的值,它会在以后变得混乱那把钥匙。对于键被专门键入为 String
的字典,那么 updateValue
函数将有一个编译时错误,指出键需要是 String
,所以你不能把事情搞砸了。
Swift 2.3
我找到了我想要的扩展解决方案。
extension Dictionary {
subscript(key: Keys) -> Value? {
get {
return self[String(key) as! Key]
}
set(value) {
guard
let value = value else {
self.removeValueForKey(String(key) as! Key)
return
}
self.updateValue(value, forKey: String(key) as! Key)
}
}
}
enum Keys {
case key_one
case key_two
}
var myStringDict = [String : String]()
/// Adding the first key value through the String() way on purpose
myStringDict.updateValue("firstValue", forKey: String(Keys.key_one))
// myStringDict: ["key_one": "firstValue"]
// myStringDict[Keys.key_one]!: firstValue
myStringDict[Keys.key_two] = "secondValue"
// myStringDict: ["key_one": "firstValue", "key_two": "secondValue"]
myStringDict[Keys.key_one] = nil
// myStringDict: ["key_two": "secondValue"]
请注意,声明的字典键类型是 String
,但我只能使用 Keys.key_one
,字典扩展中的下标会处理其余部分。
我可能可以更好地保护 as!
到 Key
的转换,但我不确定是否需要它,因为我知道我的枚举总是可以转换为有效的 Key
由 String()
演员。
改进回答
更好的是,因为我将它用于 API 键,所以我制作了一个名为 APIKeys
的空白协议,每个模型将实现自己的 Keys
枚举,该枚举符合APIKeys
协议。字典的下标更新为将 APIKeys
作为 Key
值。
extension Dictionary {
subscript(key: APIKeys) -> Value? {
get {
return self[String(key) as! Key]
}
set(value) {
guard
let value = value else {
self.removeValueForKey(String(key) as! Key)
return
}
self.updateValue(value, forKey: String(key) as! Key)
}
}
}
protocol APIKeys {}
enum Keys: APIKeys {
case key_one
case key_two
}
enum Model1Keys: APIKeys {
case model_1_key_one
case model_1_key_two
}
enum Model2Keys: APIKeys {
case model_2_key_one
case model_2_key_two
}
var myStringDict = [String : String]()
var model1StringDict = [String : String]()
var model2StringDict = [String : String]()
myStringDict.updateValue("firstValue", forKey: String(Keys.key_one)) // myStringDict: ["key_one": "firstValue"]
myStringDict[Keys.key_two] = "secondValue" // myStringDict: ["key_one": "firstValue", "key_two": "secondValue"]
myStringDict[Keys.key_one] = nil // myStringDict: ["key_two": "secondValue"]
model1StringDict.updateValue("firstValue", forKey: String(Model1Keys.model_1_key_one)) // model1StringDict: ["model_1_key_one": "firstValue"]
model1StringDict[Model1Keys.model_1_key_two] = "secondValue" // model1StringDict: ["model_1_key_one": "firstValue", "model_1_key_two": "secondValue"]
model1StringDict[Model1Keys.model_1_key_one] = nil // model1StringDict: ["model_1_key_two": "secondValue"]
model2StringDict.updateValue("firstValue", forKey: String(Model2Keys.model_2_key_one)) // model2StringDict: ["model_2_key_one": "firstValue"]
model2StringDict[Model2Keys.model_2_key_two] = "secondValue" // model2StringDict: ["model_2_key_one": "firstValue", "model_2_key_two": "secondValue"]
model2StringDict[Model2Keys.model_2_key_one] = nil // model2StringDict: ["model_2_key_two": "secondValue"]
如果我这样声明 enum
:
enum Keys {
case key_one
case key_two
}
我可以打印它会自动转换成String
:
print(Keys.key_one) // prints "key_one"
如果我然后制作一个将 Strings
映射到任何内容的字典(但为了简单起见,让我们再次选择 Strings
),我认为它应该能够通过使用 [=18 添加一个键=] 作为关键,对吧?错了。
var myDict = [String : String]()
/// error: cannot subscript a value of type '[String : String]' with an index
/// of type 'Keys'
myDict[Keys.key_one] = "firstKey"
如果我明确地将 Keys.key_one
转换为 String
,我可以做到这一点:
myDict[String(Keys.key_one)] = "firstKey"
print(myDict) // ["key_one": "firstKey"]
所以我想这样做而不必每次都用 String()
包装我的 enum
。
我尝试了一些事情,通过使用 where
关键字和 Key
从 Dictionary
中执行 extension
并尝试实现新的 subscript
功能,但我无法让它工作。有什么诀窍?
解决方案更新
我找到了我的解决方案 (
您的键是字符串有多重要?如果它不重要,我建议做这样的事情:
enum Keys {
case key_one
case key_two
}
var myDict = [Keys : String]()
myDict[Keys.key_one] = "firstKey"
print(myDict) // [Keys.key_one: "firstKey"]
或者你可以这样做:
enum Keys: String {
case key_one
case key_two
}
并像这样使用它:
var myDict = [String : String]()
myDict[Keys.key_one.rawValue] = "firstKey"
更新
如果你真的想让它工作,你可以按照下面的方法做。但这些都是强制展开的一些风险。
extension Dictionary where Key: StringLiteralConvertible {
subscript (key: Keys) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
然后你像这样使用它:
myDict[Keys.key_one] = "firstKey"
myDict[Keys.key_one]
Swift 4.2
的更新和改进extension Dictionary {
subscript(key: APIKeys) -> Value? {
get {
guard let key = key.stringValue as? Key else { return nil }
return self[key]
}
set(value) {
guard let key = key.stringValue as? Key else { return }
guard let value = value else { self.removeValue(forKey: key); return }
self.updateValue(value, forKey: key)
}
}
}
protocol APIKeys {}
extension APIKeys {
var stringValue: String {
return String(describing: self)
}
}
enum Keys: APIKeys {
case key_one
case key_two
}
var myStringDict = [AnyHashable : Any]()
var model1StringDict = [String : Any]()
var model2StringDict = [String : String]()
myStringDict.updateValue("firstValue", forKey: Keys.key_one.stringValue) // [key_one: firstValue]
myStringDict[Keys.key_two] = "secondValue" // [key_two: secondValue, key_one: firstValue]
myStringDict[Keys.key_one] = nil // [key_two: secondValue]
myStringDict.removeValue(forKey: Keys.key_two.stringValue) // []
model1StringDict.updateValue("firstValue", forKey: Model1Keys.model_1_key_one.stringValue) // [model_1_key_one: firstValue]
model1StringDict[Model1Keys.model_1_key_two] = "secondValue" // [model_1_key_two: secondValue, model_1_key_one: firstValue]
model1StringDict[Model1Keys.model_1_key_one] = nil // [model_1_key_two: secondValue]
model2StringDict.updateValue("firstValue", forKey: Model2Keys.model_2_key_one.stringValue) // [model_2_key_one: firstValue]
model2StringDict[Model2Keys.model_2_key_two] = "secondValue" // [model_2_key_two: secondValue, model_2_key_one: firstValue]
model2StringDict[Model2Keys.model_2_key_one] = nil // [model_2_key_two: secondValue]
我专门更改了 3 个词典的类型以显示键入词典的常用方式([AnyHashable : Any]
、[String : Any]
和 [String : String]
),并表明这适用于每种类型。
需要注意的是,当你的字典的键是AnyHashable
时,如果你使用updateValue
而不是赋值运算符,那么你需要指定String
值用 .stringValue
键。否则,存储的密钥的确切类型将不会明确地是 String
,如果您尝试通过将 nil
分配给删除密钥下的值,它会在以后变得混乱那把钥匙。对于键被专门键入为 String
的字典,那么 updateValue
函数将有一个编译时错误,指出键需要是 String
,所以你不能把事情搞砸了。
Swift 2.3
我找到了我想要的扩展解决方案。
extension Dictionary {
subscript(key: Keys) -> Value? {
get {
return self[String(key) as! Key]
}
set(value) {
guard
let value = value else {
self.removeValueForKey(String(key) as! Key)
return
}
self.updateValue(value, forKey: String(key) as! Key)
}
}
}
enum Keys {
case key_one
case key_two
}
var myStringDict = [String : String]()
/// Adding the first key value through the String() way on purpose
myStringDict.updateValue("firstValue", forKey: String(Keys.key_one))
// myStringDict: ["key_one": "firstValue"]
// myStringDict[Keys.key_one]!: firstValue
myStringDict[Keys.key_two] = "secondValue"
// myStringDict: ["key_one": "firstValue", "key_two": "secondValue"]
myStringDict[Keys.key_one] = nil
// myStringDict: ["key_two": "secondValue"]
请注意,声明的字典键类型是 String
,但我只能使用 Keys.key_one
,字典扩展中的下标会处理其余部分。
我可能可以更好地保护 as!
到 Key
的转换,但我不确定是否需要它,因为我知道我的枚举总是可以转换为有效的 Key
由 String()
演员。
改进回答
更好的是,因为我将它用于 API 键,所以我制作了一个名为 APIKeys
的空白协议,每个模型将实现自己的 Keys
枚举,该枚举符合APIKeys
协议。字典的下标更新为将 APIKeys
作为 Key
值。
extension Dictionary {
subscript(key: APIKeys) -> Value? {
get {
return self[String(key) as! Key]
}
set(value) {
guard
let value = value else {
self.removeValueForKey(String(key) as! Key)
return
}
self.updateValue(value, forKey: String(key) as! Key)
}
}
}
protocol APIKeys {}
enum Keys: APIKeys {
case key_one
case key_two
}
enum Model1Keys: APIKeys {
case model_1_key_one
case model_1_key_two
}
enum Model2Keys: APIKeys {
case model_2_key_one
case model_2_key_two
}
var myStringDict = [String : String]()
var model1StringDict = [String : String]()
var model2StringDict = [String : String]()
myStringDict.updateValue("firstValue", forKey: String(Keys.key_one)) // myStringDict: ["key_one": "firstValue"]
myStringDict[Keys.key_two] = "secondValue" // myStringDict: ["key_one": "firstValue", "key_two": "secondValue"]
myStringDict[Keys.key_one] = nil // myStringDict: ["key_two": "secondValue"]
model1StringDict.updateValue("firstValue", forKey: String(Model1Keys.model_1_key_one)) // model1StringDict: ["model_1_key_one": "firstValue"]
model1StringDict[Model1Keys.model_1_key_two] = "secondValue" // model1StringDict: ["model_1_key_one": "firstValue", "model_1_key_two": "secondValue"]
model1StringDict[Model1Keys.model_1_key_one] = nil // model1StringDict: ["model_1_key_two": "secondValue"]
model2StringDict.updateValue("firstValue", forKey: String(Model2Keys.model_2_key_one)) // model2StringDict: ["model_2_key_one": "firstValue"]
model2StringDict[Model2Keys.model_2_key_two] = "secondValue" // model2StringDict: ["model_2_key_one": "firstValue", "model_2_key_two": "secondValue"]
model2StringDict[Model2Keys.model_2_key_one] = nil // model2StringDict: ["model_2_key_two": "secondValue"]