使用枚举的默认值作为字典键而不显式转换为String

Tim*_*qua 5 enums dictionary key swift

如果我宣布enum这样的话:

enum Keys {
    case key_one
    case key_two
}
Run Code Online (Sandbox Code Playgroud)

我可以打印它,它会自动转换为String:

print(Keys.key_one) // prints "key_one"
Run Code Online (Sandbox Code Playgroud)

如果我然后制作一个映射Strings到任何地方的字典(但Strings为了简单起见让我们再次选择),我认为它应该能够通过使用键来添加一个Keys.key_one键,对吧?错误.

var myDict = [String : String]()

/// error: cannot subscript a value of type '[String : String]' with an index
/// of type 'Keys'
myDict[Keys.key_one] = "firstKey"
Run Code Online (Sandbox Code Playgroud)

如果我明确转换Keys.key_one为这样的话,我可以这样做String:

myDict[String(Keys.key_one)] = "firstKey"
print(myDict) // ["key_one": "firstKey"]
Run Code Online (Sandbox Code Playgroud)

所以,我想这样做,而不必换我enumString()每一次.

我已经做了尝试了几件事情extension的关闭Dictionary使用where与关键字Key,并试图实现新subscript的功能,但我不能得到它的工作.有什么诀窍?

更新解决方案

我找到了解决方案(见下文).

Tim*_*qua 7

更新和改进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]
Run Code Online (Sandbox Code Playgroud)

我特意换了类型3个字典的显示输入字典(常见方式[AnyHashable : Any],[String : Any][String : String]),并表明这工作与每一个类型的.

重要的是要注意,如果updateValue在字典的键是时使用而不是赋值运算符AnyHashable,则需要指定String键的值.stringValue.否则,存储的密钥的确切类型将不会显式为a String,如果您尝试通过分配nil给该密钥来删除密钥下的值,则它将在以后被搞乱.对于专门键入键的字典String,那么该updateValue函数将有一个编译时错误,表明该键需要为a String,所以你不能把它搞乱.

斯威夫特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"]
Run Code Online (Sandbox Code Playgroud)

请注意,声明的字典键类型是String,但我可以使用Keys.key_one,字典扩展中的下标负责其余部分.

我可以更好地保护as!转换到Key,但我不确定它是否需要,因为我知道我的枚举总是可以KeyString()演员转换为有效.

改进回答

更好的是,因为我将它用于API密钥,所以我调用了一个空白协议APIKeys,每个模型都会实现Keys符合APIKeys协议的枚举.并且字典的下标被更新APIKeysKey值.

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"]
Run Code Online (Sandbox Code Playgroud)