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
。下标是为类型
字典
定义的。如果要使用此解决方案的示例,请参见此处:@mluisbrown
dic[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
        }
    }
}