【问题标题】:Subscript: access my dictionary values with a String enumeration下标:使用字符串枚举访问我的字典值
【发布时间】:2017-01-22 18:33:27
【问题描述】:

我想做这样的事情:使用字符串枚举访问我的字典值。我试图重载字典的下标但没有成功。

访问字典:

let district = address[JsonKeys.district]

JsonKeys 在哪里:

enum JsonKeys: String {
    case key1
    case key2
    case key...
}

我的下标重载如下:

extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
    subscript(index: FOJsonKeys) -> AnyObject {
        get {
            return self[ index.rawValue] as! AnyObject
        }
    }
}

我收到以下消息:

**Cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String'**

我哪里错了?

PS:不想这样做(这会纠正错误,但这样代码不可读):

let district = address[JsonKeys.district.rawValue]

字典是 AlamoFire 给我的 Json 解析字典。我很确定我不能改变它的类型。

【问题讨论】:

  • 这可能指向更深层次的问题。您应该尽快将这些字典解析为结构或类,因此必须处理 JSON 字符串键的代码应该非常本地化。如果您需要在程序中的多个位置使用这些键,则您的模型层可能不正确。失败的原因是“StringLiteralConvertible”不是完全字符串。目前不可能在 Swift 中编写这个扩展(这是一个已知的编译器限制,但是当你需要它时,你几乎总是在做其他错误)。
  • 这就是这段代码的目标,将Json字典解析成一个类。我不想使用字符串键。在我看来,使用 String 枚举更健壮。
  • 这很公平;在下面回答。
  • 请注意,在 Swift 3.1 中,支持具体的同类型要求,所以你可以写 extension Dictionary where Key == String :)

标签: ios swift dictionary overloading subscript


【解决方案1】:

我知道这是一个老问题,但我想我应该添加一个更易于扩展、重用和更轻量级的实现

public protocol UsesRawValue {
    var rawValue: String { get }
}

extension JsonKeys: UsesRawValue {}

extension Dictionary where Key: ExpressibleByStringLiteral {
    public subscript(key: UsesRawValue) -> Value? {
        get { return self[key.rawValue as! Key] }
        set { self[key.rawValue as! Key] = newValue }
    }
}

Based on this blog post

这种方法只需要我们扩展我们的字典一次,而不是每个枚举。相反,每个枚举都需要符合UsesRawValue。现在我们可以这样使用它了。

ajson[JsonKeys.key1]
ajson[JsonKeys.key1] = "name"

【讨论】:

    【解决方案2】:

    最简单的方法是将字典提升到更多上下文中。这种情况下的上下文是“它只有来自这个枚举的键”。在 Swift 中提升类型非常简单。只需将其包装在结构中即可。

    // This could be a nested type inside JSONObject if you wanted.
    enum JSONKeys: String {
        case district
    }
    
    // Here's my JSONObject. It's much more type-safe than the dictionary,
    // and it's trivial to add methods to it.
    struct JSONObject {
        let json: [String: AnyObject]
        init(_ json: [String: AnyObject]) {
            self.json = json
        }
    
        // You of course could make this generic if you wanted so that it
        // didn't have to be exactly JSONKeys. And of course you could add
        // a setter.
        subscript(key: JSONKeys) -> AnyObject? {
            return json[key.rawValue]
        }
    }
    
    let address: [String: AnyObject] = ["district": "Bob"]
    
    // Now it's easy to lift our dictionary into a "JSONObject"
    let json = JSONObject(address)
    
    // And you don't even need to include the type. Just the key.
    let district = json[.district]
    

    【讨论】:

      【解决方案3】:

      试试这个:

      extension Dictionary where Key: StringLiteralConvertible {
          subscript(index: JsonKeys) -> Value {
              get {
                  return self[index.rawValue as! Key]!
              }
          }
      }
      

      请记住,在约束为Key: StringLiteralConvertible 的情况下,扩展适用于任何其键符合StringLiteralConvertible 的字典。 (你知道String以外的很多类型都符合StringLiteralConvertible。)

      要调用下标self[],您需要传递一个Key 类型的值。 index.rawValueString,在扩展程序中可能并不总是 Key

      因此,我展示的扩展程序适用于某些词典,但会导致某些其他词典运行时崩溃。


      更多类型安全的方式:

      protocol MyJsonKeysConvertible {
          init(jsonKeys: JsonKeys)
      }
      extension String: MyJsonKeysConvertible {
          init(jsonKeys: JsonKeys) {self = jsonKeys.rawValue}
      }
      extension Dictionary where Key: MyJsonKeysConvertible {
          subscript(index: JsonKeys) -> Value {
              get {
                  return self[Key(jsonKeys: index)]!
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多