Swift中基于字符串枚举的下标字典
我想用Swift中基于字符串枚举的下标字典,swift,generics,dictionary,enums,swift-extensions,Swift,Generics,Dictionary,Enums,Swift Extensions,我想用String键(JSON字典)扩展Dictionary,以允许订阅具有RawValue类型的String的任何enum。最终目标是多个enum,可用于为JSON字典下标 enum JSONKey: String { case one, two, three } enum OtherJSONKey: String { case a, b, c } if let one = jsonDictionary[.one] { /* ... */ } if let b = json
String
键(JSON字典)扩展Dictionary
,以允许订阅具有RawValue
类型的String
的任何enum
。最终目标是多个enum
,可用于为JSON字典下标
enum JSONKey: String {
case one, two, three
}
enum OtherJSONKey: String {
case a, b, c
}
if let one = jsonDictionary[.one] { /* ... */ }
if let b = jsonDictionary[.b] { /* ... */ }
但我不知道如何实现这一点。我知道我需要扩展字典
,但无法找出通用扩展约束或方法扩展约束
我的第一个想法是尝试向下标方法添加通用约束。不过,我认为下标方法不允许泛型
extension Dictionary {
subscript<T: RawRepresentable>(key: T) -> Value? { /* ... */ }
}
更新:
我已经玩了一些,并得到了一点接近。下面的扩展可以编译,但如果我实际尝试使用它,则会出现“下标不明确”错误
extension Collection where Iterator.Element == (key: String, value: AnyObject) {
// Compiles but can't be used because of ambiguous subscript.
subscript(key: CustomStringConvertible) -> AnyObject? {
guard let i = index(where: { $0.key == key.description }) else { return nil }
return self[i].value
}
}
@泰坦尼克号的答案是有效的,所以除非有人能想出更好的答案,否则它将是公认的答案。所以这对我来说是有效的:
enum JSONKey: String {
case one
case two
case three
}
extension Dictionary {
subscript(key: JSONKey) -> Value {
get {
let k = key.rawValue as! Key
return self[k]!
}
set {
let k = key.rawValue as! Key
self[k] = newValue
}
}
}
var jsonDictionary = [JSONKey.one.rawValue : "hello", JSONKey.two.rawValue : "hi there", JSONKey.three.rawValue : "foobar", "fourth value" : 4]
let one = jsonDictionary[.one]
let two = jsonDictionary[.two]
var three = jsonDictionary[.three]
let four = jsonDictionary["fourth value"]
jsonDictionary[.three] = 5
three = jsonDictionary[.three]
print("One: \(one), Two: \(two), Three: \(three), Four: \(four!)")
它会打印:
"One: hello, Two: hi there, Three: 5, Four: 4\n"
据我所知,您希望在任何带有字符串键的字典上都有一个扩展,以允许使用带有字符串的枚举作为其原始值类型进行订阅。如果是这样的话,以下内容应该适用于您:
enum JSONKey: String {
case one, two, three
}
class JSONObject { }
extension Dictionary where Key: StringLiteralConvertible {
subscript(jsonKey: JSONKey) -> Value? {
get {
return self[jsonKey.rawValue as! Key]
}
set {
self[jsonKey.rawValue as! Key] = newValue
}
}
}
var jsonDict: [String: AnyObject] = [:]
jsonDict[JSONKey.one] = JSONObject()
jsonDict["two"] = JSONObject()
print(jsonDict["one"]!)
print(jsonDict[JSONKey.two]!)
如果您想将其扩展为适用于任何以String作为其RawValue类型的枚举,则需要泛型。由于Swift不支持通用下标(请参阅),因此需要获取/设置方法或属性:
enum AnotherEnum: String {
case anotherCase
}
extension Dictionary where Key: StringLiteralConvertible {
func getValue<T: RawRepresentable where T.RawValue == String>(forKey key: T) -> Value? {
return self[key.rawValue as! Key]
}
mutating func setValue<T: RawRepresentable where T.RawValue == String>(value: Value, forKey key: T) {
self[key.rawValue as! Key] = value
}
}
jsonDict.setValue(JSONObject(), forKey: AnotherEnum.anotherCase)
print(jsonDict.getValue(forKey: AnotherEnum.anotherCase)!)
enum AnotherEnum:String{
个案
}
扩展字典,其中键:StringLiteralConvertible{
func getValue(forKey:T)->值{
返回self[key.rawValue as!key]
}
变异func setValue(值:value,forKey:T){
self[key.rawValue as!key]=值
}
}
setValue(JSONObject(),forKey:AnotherEnum.anotherCase)
打印(jsonDict.getValue(forKey:anothernum.anotherCase)!)
借助Swift 4对的支持,您现在可以执行以下操作:
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript<Index: RawRepresentable>(index: Index) -> Value? where Index.RawValue == String {
get {
return self[index.rawValue as! Key]
}
set {
self[index.rawValue as! Key] = newValue
}
}
}
扩展字典,其中键:ExpressibleByStringLiteral{
下标(index:index)->Value?其中index.RawValue==String{
得到{
返回self[index.rawValue as!Key]
}
设置{
self[index.rawValue as!Key]=newValue
}
}
}
允许您使用任何字符串作为其RawValue
类型的枚举:
let value=jsonDict[JSONKey.one]
这将适用于任何字符串枚举,而不仅仅是
[String:String]
字典中的JSONKey,您将无法使用.one
为其下标。您必须使用JSONKey.one
来执行此操作(除了您正在询问的扩展名之外)。这仍然很好。这允许为JSON键创建一个名称空间,而不是键入JSON[“someKey”]
或保留一堆String
常量。查看我的答案。虽然我认为这无助于解决您的问题,但您可以将多个where子句与逗号组合起来:where Key:RawRepresentable,Key.RawValue==String
好答案。仅供参考,在setter中,您不需要传递它值
。newValue
关键字涵盖了这一点(如我的答案所示)。这看起来是一个很好的解决方案,但我似乎在实现中遗漏了一个步骤。当我使用上面的字典扩展时,请声明一个字符串键的枚举,如:enum key:String{case daily=“daily”case current=“current”case temperature=“temperature”case summary=“summary”},然后尝试在代码中使用它,如:let temperature=json[Keys.current][Keys.temperature.double我被一个错误标记为:“无法用“WeatherDetail.Keys”类型的索引为“JSON”类型的值下标。@Gallaugher无法为“JSON”类型的值下标。
。这里的问题是JSON
。下标是为类型字典定义的。如果要使用此解决方案的示例,请参见此处:@mluisbrowndic[JSONKey.one]=JSONKey.two这仍然会导致字典值存储枚举而不是原始值。
enum AnotherEnum: String {
case anotherCase
}
extension Dictionary where Key: StringLiteralConvertible {
func getValue<T: RawRepresentable where T.RawValue == String>(forKey key: T) -> Value? {
return self[key.rawValue as! Key]
}
mutating func setValue<T: RawRepresentable where T.RawValue == String>(value: Value, forKey key: T) {
self[key.rawValue as! Key] = value
}
}
jsonDict.setValue(JSONObject(), forKey: AnotherEnum.anotherCase)
print(jsonDict.getValue(forKey: AnotherEnum.anotherCase)!)
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript<Index: RawRepresentable>(index: Index) -> Value? where Index.RawValue == String {
get {
return self[index.rawValue as! Key]
}
set {
self[index.rawValue as! Key] = newValue
}
}
}