Swift中基于字符串的枚举的下标字典

时间:2016-08-04 18:24:26

标签: swift generics dictionary enums swift-extensions

我希望使用Dictionary密钥(JSON词典)扩展String,以允许订阅enum RawValue类型String的任何enums。最终目标是多个enum JSONKey: String { case one, two, three } enum OtherJSONKey: String { case a, b, c } if let one = jsonDictionary[.one] { /* ... */ } if let b = jsonDictionary[.b] { /* ... */ } ,可用于下标JSON词典。

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] { /* ... */ } 创建协议。我知道有些事情无法做到,但认为值得一提的是这个想法。所以,再次,把它放在无效的代码中:

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
    }

}

更新

我已经玩了一些,并且更接近了一点。下面的扩展编译,但如果我真的尝试使用它,会给我一个“下标是模糊的”错误。

<ScrollViewer x:Name="scrollViewer" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollMode="Disabled" VerticalScrollBarVisibility="Hidden">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="VisualStateGroup">
            <VisualState x:Name="SlidButtonTop">
                <VisualState.Setters>
                    <Setter Target="SlidButtonText.Text" Value="&#xE74B;"/>
                </VisualState.Setters>
            </VisualState>
            <VisualState x:Name="SlidButtonBottom">
                <VisualState.Setters>
                    <Setter Target="SlidButtonText.Text" Value="&#xE74A;"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="20" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Border x:Name="Area1" Grid.Row="0" Height="{x:Bind childheight}" HorizontalAlignment="Stretch" Background="Transparent" 
                Child="{x:Bind TopContent, Mode=OneWay}"></Border>
        <Grid x:Name="SlidButton" Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1"
          ManipulationStarted="SlidButton_ManipulationStarted" ManipulationCompleted="SlidButton_ManipulationCompleted" 
          ManipulationMode="All" ManipulationDelta="SlidButton_ManipulationDelta">
            <TextBlock x:Name="SlidButtonText" Text="&#xE74A;" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontFamily="Segoe MDL2 Assets" FontSize="15" />
        </Grid>
        <Border x:Name="Area2" Grid.Row="2" Height="{x:Bind childheight}" HorizontalAlignment="Stretch" Background="Transparent"
                Child="{x:Bind BottomContent, Mode=OneWay}"></Border>
    </Grid>
</ScrollViewer>

@ titaniumdecoy的答案是有效的,除非有人能提出更好的建议,否则这将是公认的答案。

3 个答案:

答案 0 :(得分:13)

借助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
        }
    }
} 

允许您使用带有字符串的任何枚举,因为它的RawValue类型:

let value = jsonDict[JSONKey.one]

这将适用于任何字符串枚举,而不仅仅是JSONKey

答案 1 :(得分:8)

据我了解,您希望对任何包含字符串键的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]!)

如果要将此扩展为任何枚举,并将String作为其RawValue类型,则需要泛型。由于Swift不支持通用下标(请参阅SR-115),因此需要获取/设置方法或属性:

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)!)

答案 2 :(得分:3)

所以这对我有用:

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"