通用可选枚举函数
Posted
技术标签:
【中文标题】通用可选枚举函数【英文标题】:Generic optional enum func 【发布时间】:2018-05-29 13:58:12 【问题描述】:在我的项目中,我有几个在任何地方都使用的枚举,这些枚举是基于外部 json 创建的,因此输入始终是可选的。如果无法从输入创建枚举,我将定义一个默认值。
我现在做事的例子:
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()
这显然有效,但我想让我的构造函数更快速、更通用并适用于更多这些枚举。我的目标是像这样实例化这些枚举:
let textAlign = TextAlign(rawOptionalValue: rawData["textAlign"] as? String) ?? TextAlign.left
所以我基本上想要一个可失败的初始化程序,我可以为 TextAlign 枚举编写它,但必须有一种方法以更“通用”的方式声明它,以便我可以在我的所有枚举上使用初始化程序:String 实例。 我在 swift 中对泛型的语法和选项有些挣扎。
有什么想法吗?
更新 #1
我看到很多答案都没有错,但可能我在寻找的内容上不够清楚。
我有更多这样的枚举:
enum TextAlign:String
case left, center, right
enum CornerRadius:String
case none, medium, large
enum Spacing:String
case tight, medium, loose
我只想定义 1 个可以初始化所有这些枚举的函数。 (不是因为我懒,而是因为我想了解如何为此使用泛型)
我可能需要的是扩展中的一些静态函数,适用于所有这些“String/RawRepresentable”枚举。我不想为每个枚举编写所有这些失败的初始化程序。 (我相信应该可以,但我不知道语法)
更新 #2
在玩了一下 Joakim 的回答后,我想出了以下解决方案:
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?
作为参数传递,并且它通过 let rawValue = value as? T
自行处理转换。
更新#3(解决方案)
好的,这一切的复杂性仍然让我有点困扰,所以我尝试了 init 路由,imo 看起来更干净。整个事情现在看起来像这样:
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
为了方便起见,我创建了一个带有默认值的可失败和不可失败的 init。
现在我可以像这样实例化任何枚举:
let cornerRadius = CornerRadius(json["cornerRadius"], or: .medium)
// or an optional one
let align = TextAlign(json["textAlign"])
现在我完成了更新...
【问题讨论】:
不清楚。您要求一种通用的方式,但是您的一些 cmets 建议您要像 TextAlign(...) 等那样进行初始化,但这不是通用的。 是的。我要求一种通用的方式,如果可能的话,我想这样初始化。如果不可能,那就不要:) 【参考方案1】:这是一个可用于从字符串创建枚举项的函数
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
【讨论】:
是的!这就是我一直在寻找的,但是!我更愿意将其称为TextAlign.create(...)
和CornerRadius.create(...)
。应该可以吧?
或者.. 更好,只需使用TextAlign(...)
和CornerRadius(...)
。不确定是否可以实际定义这样的初始化程序。
当然是的,但它不会是通用的,如果你想创建一个像这样的 init 方法,它只是默认方法的简单包装。
感谢您的输入,当我从您的最终解决方案中得出最终解决方案时,我会将您的答案标记为已接受。【参考方案2】:
我认为您不需要为此添加新功能。只需使用Optional.map(_:)
:
let alignment = rawData["textAlign"].map(TextAlign.init(rawValue:)) ?? .left
【讨论】:
我不喜欢它,但我更喜欢更简洁的初始化程序(请参阅更新 #3)。谢谢。 @GertjanSmits 够公平的。但我不鼓励or: .medium
论点。 Nil 合并是一个简洁且易于理解的特征。我会推荐CornerRadius(json["cornerRadius"]) ?? .medium
而不是CornerRadius(json["cornerRadius"], or: .medium)
我同意,但这确实是一件方便的事情。出于这个原因,我还创建了init?()
。我必须为工作选择正确的init
,有时可选的是我想要的,在其他情况下,我只想在未包装的(默认)枚举上选择switch
。
没错。 Swift 有很多处理选项的工具。 (!
、??
、条件绑定、模式匹配、Optional.map
、Optional.flatMap
等)。围绕这些通用设施制作包装 API 是没有意义的【参考方案3】:
你可以像这样声明你的枚举
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
【讨论】:
当它永远不可能失败时,为什么要让这个初始化成为一个可失败的初始化? 所有都非常有效,但不是我想要的,请在我的问题中查看我的更新。 我明白了。根据您的更新,我添加了另一个“解决方案”:) 哈,几乎一模一样。不错的解决方案 :) 谢谢!【参考方案4】:你可以试试这个:
let textAlign = TextAlign(rawValue: rawData["textAlign"] as? String ?? TextAlign.left.rawValue)
【讨论】:
我想到了这一点,并在某个时候得到了它,但它还不够好:)。在这种情况下,来自rawData
的值可以是String
,但这并不意味着它是有效的。在您的示例中,let textAlign
仍然是可选的 TextAlign
实例,因为 TextAlign:rawValue
初始化程序是一个可失败的实例。这意味着您必须在行尾附加 ?? TextAlign.left
才能解开它。我认为这让它有点混乱。【参考方案5】:
你可以像这样定义一个静态函数,并在代码中任何你需要的地方使用它
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
【讨论】:
以上是关于通用可选枚举函数的主要内容,如果未能解决你的问题,请参考以下文章