Swift 中带有基于字符串的枚举的下标字典
Subscript Dictionary with String-based Enums in Swift
我想用 String
键(JSON 字典)扩展 Dictionary
以允许使用任何具有 RawValue
类型的 enum
进行下标=17=]。最终目标是多个 enums
可用于下标 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] { /* ... */ }
但我不知道如何实现它。我知道我需要扩展 Dictionary
,但无法弄清楚通用扩展约束或方法扩展约束。
我的第一个想法是尝试为下标方法添加一个泛型约束。不过,我不认为下标方法允许泛型。
extension Dictionary {
subscript<T: RawRepresentable>(key: T) -> Value? { /* ... */ }
}
即使对下标设置通用约束有效,我仍然需要一种嵌套通用约束的方法。或者将字典限制为基于字符串的枚举的键。要将其放入无效的代码中,我想这样做:
extension Dictionary where Key: RawRepresentable where RawValue == String {
subscript(key: Key) -> Value { /* ... */ }
}
// or
extension Dictionary {
subscript<T: RawRepresentable where RawValue == String>(key: T) -> Value { /* ... */ }
}
是否可以扩展 Dictionary
以接受基于字符串的枚举作为下标?
我关于如何实现类似内容的其他想法包括 enum
继承和为我想用作下标的特定 enums
创建协议。我知道其中一些无法完成,但认为值得一提的想法。因此,再次将其放入无效代码中:
enum JSONKey: String {}
enum NumbersJSONKey: JSONKey {
case one, two, three
}
enum LettersJSONKey: JSONKey {
case a, b, c
}
// or
protocol JSONKeys {}
enum NumbersJSONKey: JSONKey {
case one, two, three
}
enum LettersJSONKey: JSONKey {
case a, b, c
}
// then subscript with
if let one = json[.one] { /* ... */ }
更新:
我已经玩了更多,并且离得更近了。下面的扩展可以编译,但如果我真的尝试使用它,就会出现 "subscript is ambiguous" 错误。
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: { [=15=].key == key.description }) else { return nil }
return self[i].value
}
}
@titaniumdecoy 的答案有效,因此它将成为公认的答案,除非有人能想出更好的答案。
所以这对我有用:
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"
据我了解,您希望对任何具有 String 键的 Dictionary 进行扩展,以允许使用以 String 作为其 RawValue 类型的枚举进行下标。如果是这样,以下内容应该对您有用:
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]!)
如果您想扩展它以适用于 any 枚举,其中 String 作为其 RawValue 类型,您需要泛型。由于 Swift 不支持通用下标(参见 SR-115),因此需要 get/set 方法或 属性:
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)!)
有了 Swift 4 对 Generic Subscripts 的支持,您现在可以这样做:
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
}
}
}
它允许您使用 any 枚举,它具有字符串,因为它是 RawValue
类型:
let value = jsonDict[JSONKey.one]
这现在适用于任何字符串枚举,而不仅仅是 JSONKey
我想用 String
键(JSON 字典)扩展 Dictionary
以允许使用任何具有 RawValue
类型的 enum
进行下标=17=]。最终目标是多个 enums
可用于下标 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] { /* ... */ }
但我不知道如何实现它。我知道我需要扩展 Dictionary
,但无法弄清楚通用扩展约束或方法扩展约束。
我的第一个想法是尝试为下标方法添加一个泛型约束。不过,我不认为下标方法允许泛型。
extension Dictionary {
subscript<T: RawRepresentable>(key: T) -> Value? { /* ... */ }
}
即使对下标设置通用约束有效,我仍然需要一种嵌套通用约束的方法。或者将字典限制为基于字符串的枚举的键。要将其放入无效的代码中,我想这样做:
extension Dictionary where Key: RawRepresentable where RawValue == String {
subscript(key: Key) -> Value { /* ... */ }
}
// or
extension Dictionary {
subscript<T: RawRepresentable where RawValue == String>(key: T) -> Value { /* ... */ }
}
是否可以扩展 Dictionary
以接受基于字符串的枚举作为下标?
我关于如何实现类似内容的其他想法包括 enum
继承和为我想用作下标的特定 enums
创建协议。我知道其中一些无法完成,但认为值得一提的想法。因此,再次将其放入无效代码中:
enum JSONKey: String {}
enum NumbersJSONKey: JSONKey {
case one, two, three
}
enum LettersJSONKey: JSONKey {
case a, b, c
}
// or
protocol JSONKeys {}
enum NumbersJSONKey: JSONKey {
case one, two, three
}
enum LettersJSONKey: JSONKey {
case a, b, c
}
// then subscript with
if let one = json[.one] { /* ... */ }
更新:
我已经玩了更多,并且离得更近了。下面的扩展可以编译,但如果我真的尝试使用它,就会出现 "subscript is ambiguous" 错误。
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: { [=15=].key == key.description }) else { return nil }
return self[i].value
}
}
@titaniumdecoy 的答案有效,因此它将成为公认的答案,除非有人能想出更好的答案。
所以这对我有用:
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"
据我了解,您希望对任何具有 String 键的 Dictionary 进行扩展,以允许使用以 String 作为其 RawValue 类型的枚举进行下标。如果是这样,以下内容应该对您有用:
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]!)
如果您想扩展它以适用于 any 枚举,其中 String 作为其 RawValue 类型,您需要泛型。由于 Swift 不支持通用下标(参见 SR-115),因此需要 get/set 方法或 属性:
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)!)
有了 Swift 4 对 Generic Subscripts 的支持,您现在可以这样做:
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
}
}
}
它允许您使用 any 枚举,它具有字符串,因为它是 RawValue
类型:
let value = jsonDict[JSONKey.one]
这现在适用于任何字符串枚举,而不仅仅是 JSONKey