通用可选枚举函数

时间:2018-05-29 13:58:12

标签: swift generics enums optional

在我的项目中,我有几个枚举,我到处使用,这些枚举是基于外部json创建的,因此输入始终是可选的。如果无法从输入创建枚举,我将定义默认值。

我现在如何做事的例子:

package ...;

public class ListingMusic implements 
RequestHandler<HashMap<String, Object>, String> {

    @Override
    public String handleRequest(HashMap<String, Object> input, Context context) {
        ...
    }

}

这显然很有用,但我想让我的构造函数更加流畅,通用并适用于更多这些枚举。我的目标是实例化这样的枚举:

enum TextAlign:String {
    case left, center, right
}

let rawData = [String:Any]()

func getTextAlign() -> TextAlign {
    if let rawTextAlignString = rawData["textAlign"] as? String, let align = TextAlign(rawValue: rawTextAlignString) {
        return align
    }

    return TextAlign.left
}
let textAlign = self.getTextAlign()

所以我基本上想要一个可用的初始化器,我可以为TextAlign枚举编写,但是必须有一种方法可以用更多的通用语言来声明它。方式,以便我可以在我的所有枚举:String实例上使用初始化程序。 我在swift中对泛型的语法和选项进行了一些改进。

有什么想法吗?

更新#1

我看到很多答案都没有错,但可能我对我所寻找的内容不够清楚。

我有更多这样的枚举:

let textAlign = TextAlign(rawOptionalValue: rawData["textAlign"] as? String) ?? TextAlign.left

我想定义一个可以初始化所有这些枚举的函数。 (不是因为我懒惰,而是因为我想了解如何使用泛型)

我需要的是扩展中的一些静态函数,它适用于所有这些&#39; String / RawRepresentable&#39;枚举。我不想为每个枚举编写所有这些可用的初始值设定项。 (我相信它应该是可能的,但我无法弄清楚语法)

更新#2

在与Joakim的回答中稍作后,我提出了以下解决方案:

enum TextAlign:String {
    case left, center, right
}

enum CornerRadius:String {
    case none, medium, large
}

enum Spacing:String {
    case tight, medium, loose
}

这允许我使用此函数实例化extension RawRepresentable { static func create<T:Any>(_ value: Any?, defaultValue: Self) -> Self where Self.RawValue == T { guard let rawValue = value as? T, let instance = Self.init(rawValue: rawValue) else { return defaultValue } return instance } } String(及更多)类型的枚举。像这样:

Int

我喜欢我可以将enum TextAlign:String { case left, center, right } enum CornerRadius:Int { case none, medium, large } let json:[String:Any] = [ "textAlign":"left", "cornerRadius":0 ] let cornerRadius = CornerRadius.create(json["cornerRadius"], defaultValue: .medium) let align = TextAlign.create(json["textAlign"], defaultValue: .center) 作为参数传递,并通过Any?来自行处理投射。

更新#3(解决方案)

好吧,它的复杂性仍然让我感到困扰,所以我尝试了初始路线,imo看起来更干净了。现在整个事情看起来像这样:

let rawValue = value as? T

为方便起见,我创建了一个可用且不可用的init,其默认值为。

现在我可以像这样实例化任何枚举:

extension RawRepresentable {
    init(from value: Any?, or defaultValue: Self) {
        self = Self.init(from: value) ?? defaultValue
    }

    init?(from value: Any?) {
        guard let rawValue = value as? Self.RawValue, let instance = Self.init(rawValue: rawValue) else {
            return nil
        }

        self = instance
    }
}

现在我完成了更新......

5 个答案:

答案 0 :(得分:2)

这是一个可用于从字符串

创建枚举项的函数
func createEnumItem<T: RawRepresentable>(_ value: String) -> T? where T.RawValue == String {
    return  T.init(rawValue: value)
}

然后像

一样使用它
let textAlign: TextAlign = createEnumItem("right")!
let radius: CornerRadius = createEnumItem("medium")!

请注意,您始终在变量声明中包含枚举类型。

当然,因为返回值是可选的,所以你需要以比我的例子更好的方式处理它。

更新

如果你总是知道你的默认值是修改后的版本

func createEnumItem<T: RawRepresentable>(_ value: String, withDefault defaultItem: T) -> T where T.RawValue == String {
    guard let item = T.init(rawValue: value) else {
        return defaultItem
    }
    return item
}

答案 1 :(得分:1)

您可以声明这样的枚举

enum TextAlign: String {
    case left, center, right

    init(rawOptionalValue: String?) {
        self = TextAlign(rawValue: rawOptionalValue ?? TextAlign.left.rawValue) ?? .left
    }
}

然后像这样实例化它:

let textAlign = TextAlign(rawOptionalValue: rawData["textAlign"] as? String)

更新

以下是一个默认值作为可选参数的示例:

enum TextAlign: String {
    case left, center, right

    init(rawOptionalValue: String?, defaultValue: TextAlign = TextAlign.left) {
        self = TextAlign(rawValue: rawOptionalValue ?? defaultValue.rawValue) ?? defaultValue
    }
}

let textAlign1 = TextAlign(rawOptionalValue: "left") // .left
let textAlign2 = TextAlign(rawOptionalValue: "right") // .right
let textAlign3 = TextAlign(rawOptionalValue: "center") // .center
let textAlign4 = TextAlign(rawOptionalValue: "notAnAlignment") // .left
let textAlign5 = TextAlign(rawOptionalValue: nil, defaultValue: .center) // .center

更新2

好的,现在我明白了。那么根据您的最新更新,您也可以这样做:

extension RawRepresentable {
    init(rawOptionalValue: Any?, defaultValue: Self) {
        guard let value = rawOptionalValue as? Self.RawValue else {
            self = defaultValue
            return
        }
        self = Self.init(rawValue: value) ?? defaultValue
    }
}

与我之前的尝试相比,此处唯一的一点是您无法提供默认defaultValue。所以你会这样使用它:

let textAlign1 = TextAlign(rawOptionalValue: "left", defaultValue: .left) // .left
let textAlign2 = TextAlign(rawOptionalValue: "right", defaultValue: .left) // .right
let textAlign3 = TextAlign(rawOptionalValue: "center", defaultValue: .left) // .center
let textAlign4 = TextAlign(rawOptionalValue: "notAnAlignment", defaultValue: .left) // .left
let textAlign5 = TextAlign(rawOptionalValue: nil, defaultValue: .center) // .center

答案 2 :(得分:1)

我认为你不需要新的功能。只需使用Optional.map(_:)

let alignment = rawData["textAlign"].map(TextAlign.init(rawValue:)) ?? .left

答案 3 :(得分:0)

你可以试试这个:

 let textAlign = TextAlign(rawValue: rawData["textAlign"] as? String ?? TextAlign.left.rawValue)

答案 4 :(得分:0)

您可以像这样定义一个静态函数,并在代码中的任何位置使用它

enum TextAlign: String {
    case left, center, right

    static func getTextAlign(rawData: [String:Any]) -> TextAlign {
        if let rawTextAlignString = rawData["textAlign"] as? String, let align = TextAlign(rawValue: rawTextAlignString) {
            return align
        }

        return TextAlign.left
    }
}

// Test
var myRawData = [String:Any]()

let textAlign1 = TextAlign.getTextAlign(rawData: myRawData) // left

myRawData["textAlign"] = "center"

let textAlign2 = TextAlign.getTextAlign(rawData: myRawData) // center