簡體   English   中英

Swift 對具有關聯值的枚舉的抽象

[英]Swift abstraction for enums with associated value

我有一個帶有關聯值ConfigurationValue的枚舉,它存儲在 map 中。 對於每個可能的枚舉值,我都有特定類型的 getter。

如何最小化這些 getter 之間的代碼重復

    enum ConfigurationValue {
        case bool(Bool)
        case int(Int)
        case string(String)
    }

    var map: [T:ConfigurationValue] = [:]

    public mutating func set(key: T, _ value: Bool) {
        map[key] = .bool(value)
    }

    public mutating func set(key: T, _ value: Int) {
        map[key] = .int(value)
    }

    public mutating func set(key: T, _ value: String) {
        map[key] = .string(value)
    }

    private func getValue(key: T) -> ConfigurationValue? {
        return map[key]
    }

    public func get(key: T) -> Bool? {
        guard let value = getValue(key: key),
              case .bool(let innerValue) = value else {
            return nil
        }
        return innerValue
    }

    public func get(key: T) -> Int? {
        guard let value = getValue(key: key),
              case .int(let innerValue) = value else {
            return nil
        }
        return innerValue
    }

    public func get(key: T) -> String? {
        guard let value = getValue(key: key),
              case .string(let innerValue) = value else {
            return nil
        }
        return innerValue
    }

枚舉似乎不是這里工作的正確工具,因為您可以通過僅轉換值來獲得相同的有用性(盡管不可否認,您不會從詳盡的開關錯誤中受益)。 如果您想使用 generics 來減少其中的一些重復,我會在 go 周圍使用 map 的包裝器,如下所示:

protocol Configurable {}
extension Int: Configurable {}
extension Bool: Configurable {}
extension String: Configurable {}

class ConfigurationMap<Key: Hashable> {
    private var map = [Key: Configurable]()

    func getValue(at key: Key) -> Configurable? {
        return map[key]
    }

    func setValue(_ value: Configurable, at key: Key) {
        map[key] = value
    }
}

這里Key將取代你的T通用。 要合並更多數據類型,只需將要使用的類型擴展為Configurable 如果您不介意變異方法, ConfigurationMap也可以是一個結構。

在 Swift 中,這不稱為“地圖”,而是稱為“字典”。 類型限制可以通過協議和顯式類型檢索來實現。

public protocol ConfigurationValue { }
extension Int: ConfigurationValue { }
extension Bool: ConfigurationValue { }
extension String: ConfigurationValue { }
public struct ConfigurationDictionary<Key: Hashable> {
  private var dictionary: [Key: ConfigurationValue] = [:]
}

public extension ConfigurationDictionary {
  subscript<Value: ConfigurationValue>(key: Key) -> Value? {
    get { dictionary[key] as? Value }
    set { dictionary[key] = newValue }
  }
}
var configurationDictionary = ConfigurationDictionary<String>()

configurationDictionary["i"] = 1
configurationDictionary["i"] as Int? // 1
configurationDictionary["i"] as Bool? // nil

configurationDictionary["b"] = true
let b: Bool? = configurationDictionary["b"] // true
let i: Int? = configurationDictionary["b"] // nil

我不會使用協議,而是在ConfigurationValue中添加兩個方法,第一個創建配置值,另一個獲取案例的內部值。

enum ConfigurationValue {
    case bool(Bool)
    case int(Int)
    case string(String)

    static func with<Value>(value: Value?) -> ConfigurationValue? {
        if let value = value as? Bool {
            return .bool(value)
        }
        if let value = value as? Int {
            return .int(value)
        }
        if let value = value as? String {
            return .string(value)
        }
        return nil
    }

    func get<Value>(ofType type: Value.Type = Value.self) -> Value? {
        switch self {
        case .bool(let bool):
            return bool as? Value
        case .int(let int):
            return int as? Value
        case .string(let string):
            return string as? Value
        }
    }
}

let boolValue: Bool? = ConfigurationValue.bool(false).get() // Optional(false)
let intValue: Int? = ConfigurationValue.bool(false).get() // nil
let stringValue: String? = ConfigurationValue.bool(false).get() // nil

let boolConfiguration: ConfigurationValue? = .with(value: true) // bool(true)
let intConfiguration: ConfigurationValue? = .with(value: 42) // int(42)
let stringConfiguration: ConfigurationValue? = .with(value: "42") // string("42")
let doubleConfiguration: ConfigurationValue? = .with(value: 42.0) // nil

現在你可以避免樣板,假設你有一個 struct Foo

struct Foo<T: Hashable> {
    var map: [T:ConfigurationValue] = [:]
    
    mutating func set<Value>(_ value: Value?, forKey key: T) {
        map[key] = .with(value: value)
    }
    
    func get<Value>(key: T) -> Value? {
        guard let value = map[key] else {
            return nil
        }
        return value.get()
    }
}

var map: [String:ConfigurationValue] = [
    "bool": .bool(false),
    "int": .int(0),
    "string": .string("string")
]

var bar = Foo(map: map)
var bool: Bool? = bar.get(key: "bool") // Optional(false)
var int: Int? = bar.get(key: "int") // 0
var string: String? = bar.get(key: "string") // string
bar.set(true, forKey: "bool")
bool = bar.get(key: "bool") // Optional(true)
bar.set(42, forKey: "int")
int = bar.get(key: "int") // 42
bar.set("Hello World!", forKey: "string")
string = bar.get(key: "string") // Hello World!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM