在 Swift 中为枚举提供默认值的最佳方法是什么?
What is the best approach to provide default value for enum in Swift?
所以我有几个枚举如下所示:
enum TrackType: String, CustomStringConvertible {
case video
case audio
case subtitles
case unsupported
var description: String {
switch self {
case .video: return "视频"
case .audio: return "音轨"
case .subtitles: return "字幕"
case .unsupported: return "不支持的 track"
}
}
// custom initializater to provide default value
// so I don't have to write:
// "TrackType.init(rawValue: value) ?? .unsupported"
// everywhere
init(rawValue: String) {
switch rawValue {
case "video": self = .video
case "audio": self = .audio
case "subtitles": self = .subtitles
default: self = .unsupported
}
}
}
// usage
let value = "foobar"
let trackType = TrackType.init(rawValue: value) // .unsupported
这种方法的缺点是我必须手动列出我编写的每个枚举的所有情况,所以我是这样的:
extension TrackType: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = .unsupported
return
}
self = validValue
}
}
// usage
let value = "foobar"
let trackType = value as TrackType // .unsupported
这样我可以避免繁琐的列表工作,但是我所有的枚举都必须符合 ExpressibleByStringLiteral,所以它仍然是重复的
我试着制定这样的协议:
protocol StringEnum: ExpressibleByStringLiteral {
static var `default`: Self { get }
init?(rawValue: String)
}
extension StringEnum {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = Self.`default`
return
}
self = validValue
}
}
// error:
// Initializer 'init(stringLiteral:)' has different argument labels from those required by protocol 'StringEnum' ('init(rawValue:)')
enum TrackType: StringEnum {
static var `default` = TrackType.unsupported
case video
case audio
case subtitles
case unsupported
}
我应该从这里去哪里?
我在 Default value for Enum in Swift 中看到了答案,但其中 none 足够方便...
您可以使用 RawRepresentable
枚举的 init(rawValue:)
初始值设定项来修复它。我还将 RawValue
限制为 String
-only 枚举。
这 确实 要求您仍将枚举标记为在 enum
本身中具有 String
原始值。
代码:
protocol StringEnum: RawRepresentable, ExpressibleByStringLiteral where RawValue == String {
static var `default`: Self { get }
}
extension StringEnum {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = Self.`default`
return
}
self = validValue
}
}
enum TrackType: String, StringEnum {
static let `default` = TrackType.unsupported
case video
case audio
case subtitles
case unsupported
}
用法:
let value: TrackType = "foobar"
print(value)
使用“foobar
”,结果为“unsupported
”。使用“video
”,结果为“video
”。它工作正常。
您可能已经知道这一点,但以防万一,作为最佳做法,请尽量避免使用字符串文字。人们通常首先使用枚举的原因是您不依赖整个代码中的字符串文字(它们容易出现编译器无法捕获的拼写错误)。
如果您确实需要检查字符串是否是特定枚举中的原始值,则不需要 init。你可以简单地写一下:
TrackType(rawValue: value)
此外,以防万一 - 我不确定这是否适用于您的用例而没有看到其余代码,但如果您想要做的是为某些枚举值提供默认描述,那么你可以做的是:
var description: String
{
switch self
{
case .video: return "视频"
case .audio: return "音轨"
case .subtitles: return "字幕"
default: return "不支持的 track" // .unsupported or any case other than the 3 above
}
}
那么用法是:
let value = "foobar"
let trackType = TrackType(rawValue: value) ?? .unsupported // .unsupported
let description = trackType.description // "不支持的 track"
我知道你的问题是希望代码尽可能简洁,我认为这已经是你应该做到的简洁了,不会牺牲代码的可读性和理解性。
所以我有几个枚举如下所示:
enum TrackType: String, CustomStringConvertible {
case video
case audio
case subtitles
case unsupported
var description: String {
switch self {
case .video: return "视频"
case .audio: return "音轨"
case .subtitles: return "字幕"
case .unsupported: return "不支持的 track"
}
}
// custom initializater to provide default value
// so I don't have to write:
// "TrackType.init(rawValue: value) ?? .unsupported"
// everywhere
init(rawValue: String) {
switch rawValue {
case "video": self = .video
case "audio": self = .audio
case "subtitles": self = .subtitles
default: self = .unsupported
}
}
}
// usage
let value = "foobar"
let trackType = TrackType.init(rawValue: value) // .unsupported
这种方法的缺点是我必须手动列出我编写的每个枚举的所有情况,所以我是这样的:
extension TrackType: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = .unsupported
return
}
self = validValue
}
}
// usage
let value = "foobar"
let trackType = value as TrackType // .unsupported
这样我可以避免繁琐的列表工作,但是我所有的枚举都必须符合 ExpressibleByStringLiteral,所以它仍然是重复的
我试着制定这样的协议:
protocol StringEnum: ExpressibleByStringLiteral {
static var `default`: Self { get }
init?(rawValue: String)
}
extension StringEnum {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = Self.`default`
return
}
self = validValue
}
}
// error:
// Initializer 'init(stringLiteral:)' has different argument labels from those required by protocol 'StringEnum' ('init(rawValue:)')
enum TrackType: StringEnum {
static var `default` = TrackType.unsupported
case video
case audio
case subtitles
case unsupported
}
我应该从这里去哪里?
我在 Default value for Enum in Swift 中看到了答案,但其中 none 足够方便...
您可以使用 RawRepresentable
枚举的 init(rawValue:)
初始值设定项来修复它。我还将 RawValue
限制为 String
-only 枚举。
这 确实 要求您仍将枚举标记为在 enum
本身中具有 String
原始值。
代码:
protocol StringEnum: RawRepresentable, ExpressibleByStringLiteral where RawValue == String {
static var `default`: Self { get }
}
extension StringEnum {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = Self.`default`
return
}
self = validValue
}
}
enum TrackType: String, StringEnum {
static let `default` = TrackType.unsupported
case video
case audio
case subtitles
case unsupported
}
用法:
let value: TrackType = "foobar"
print(value)
使用“foobar
”,结果为“unsupported
”。使用“video
”,结果为“video
”。它工作正常。
您可能已经知道这一点,但以防万一,作为最佳做法,请尽量避免使用字符串文字。人们通常首先使用枚举的原因是您不依赖整个代码中的字符串文字(它们容易出现编译器无法捕获的拼写错误)。
如果您确实需要检查字符串是否是特定枚举中的原始值,则不需要 init。你可以简单地写一下:
TrackType(rawValue: value)
此外,以防万一 - 我不确定这是否适用于您的用例而没有看到其余代码,但如果您想要做的是为某些枚举值提供默认描述,那么你可以做的是:
var description: String
{
switch self
{
case .video: return "视频"
case .audio: return "音轨"
case .subtitles: return "字幕"
default: return "不支持的 track" // .unsupported or any case other than the 3 above
}
}
那么用法是:
let value = "foobar"
let trackType = TrackType(rawValue: value) ?? .unsupported // .unsupported
let description = trackType.description // "不支持的 track"
我知道你的问题是希望代码尽可能简洁,我认为这已经是你应该做到的简洁了,不会牺牲代码的可读性和理解性。