简体   繁体   English

Swift 中 Enum 的默认值

[英]Default value for Enum in Swift

I have an enum :我有一个enum

public enum PersonType:String {

 case Cool                       = "cool"
 case Nice                       = "rude"
 case SoLazy                     = "so-lazy"

 public var description: String {
    switch self {
    case .Cool:
        return "Cool person"
    case .Nice:
        return "Nice person"
    case .SoLazy:
        return "its so lazy person"
    }
}


 public var typeImage: String {
    switch self {
    case .Cool:
        return "cool.png"
    case .Nice:
        return "img_nice.png"
    case .Solazy:
        return "lazy.png"
    }
   }  

}

The problem I don't know all the person type keys so I need to handle a default case of type person and to give it the description will be it's key like "so-lazy" and a default image.问题我不知道所有的 person 类型键,所以我需要处理一个 person 类型的默认情况,并给它描述将是它的键,如“so-lazy”和默认图像。

let's say I get this result from the web service:假设我从 Web 服务中得到了这个结果:

[
    {
        name: "john",
        key: "cool"
    },
    {
        name: "paul",
        key: "funny"
    }
]

I need to have aa default case to handle the key "funny"我需要有一个默认情况来处理关键“有趣”

here is how I init my enum while parsing and creating person object:这是我在解析和创建 person 对象时初始化我的枚举的方式:

if let personType = PersonType(rawValue:personTypeKey ?? "") {
   self.personType = personType
}

I want an else or a better approach to handle the case of unknown keys in my enum, and give them the key as description and a default image.我想要else或更好的方法来处理枚举中未知键的情况,并将键作为描述和默认图像。

Another approach that works in Swift 3 (maybe 2, don't know):另一种适用于 Swift 3 的方法(可能是 2,不知道):

enum PersonType: String {
    case cool = "cool"
    case nice = "nice"
    case soLazy = "so-lazy"
    case other
}

let person = PersonType(rawValue: "funny") ?? .other

The person variable is of type PersonType.other in this case.在这种情况下,person 变量的类型为 PersonType.other。

The downside to this is that you don't know the raw string value of the .other case.这样做的缺点是您不知道 .other 案例的原始字符串值。

Drop the raw type, and use enum with associated value:删除原始类型,并使用带有关联值的enum

public enum PersonType {
    case Cool
    case Nice
    case SoLazy
    case Unknown(String)
    static func parse(s:String) -> PersonType {
        switch s {
            case "Cool" : return .Cool
            case "Nice" : return .Nice
            case "SoLazy" : return .SoLazy
            default: return Unknown(s)
        }
    }
}

The downside to dropping the raw type is that you must provide some logic for parsing the known enum values.删除原始类型的缺点是您必须提供一些逻辑来解析已知的enum值。 The upside, however, is that you can fit anything else into a single Unknown case, while keeping the actual "unknown" value available for later use.但是,好处是您可以将任何其他内容放入单个Unknown案例中,同时保留实际的“未知”值以供以后使用。

This goes pretty close but I would like to be able to store the value that can be associated with it, kind of like you can with C.这非常接近,但我希望能够存储可以与之关联的值,就像使用 C 一样。

enum Errors: Int {
    case transactionNotFound = 500
    case timeout = -1001
    case invalidState = 409
    case notFound = 404
    case unknown

    init(value: Int) {
        if let error = Errors(rawValue: value) {
            self = error
        } else {
            self = .unknown
        }
    }
}

Errors(value: 40) // .unknown
Errors(value: 409) // .invalidState
Errors(value: 500) // .transactionNotFound

Had to create a custom initializer, otherwise it is recursive.必须创建一个自定义初始化程序,否则它是递归的。 And it is still possible to create using the rawValue initializer by accident.并且仍然有可能意外地使用 rawValue 初始值设定项进行创建。

This however feels more Swifty, I removed the : Int type specifier which allows you to use associated values, now the exceptional case that we don't do anything special is handled in the other :然而,这感觉更 Swifty,我删除了: Int类型说明符,它允许您使用关联的值,现在我们不做任何特殊事情的特殊情况在other处理:

enum Errors2 {
    case transactionNotFound
    case timeout
    case invalidState
    case notFound
    case other(Int)

    init(rawValue: Int) {
        switch rawValue {
        case 500:
            self = .transactionNotFound
        case -1001:
            self = .timeout
        case 409:
            self = .invalidState
        case 404:
            self = .notFound
        default:
            self = .other(rawValue)
        }
    }
}

Errors2(rawValue: 40) // .other(40)
Errors2(rawValue: 409) // .invalidState
Errors2(rawValue: 500) // .transactionNotFound
Errors2(rawValue: -1001) // .timeout

With this I could get the actual value for an "other" error, and I can use the rawValue so it acts a lot like an Int based enum.有了这个,我可以获得“其他”错误的实际值,并且我可以使用 rawValue 因此它的行为很像基于 Int 的枚举。 There is the single case statement to map the names but from then on you can use the names and never need to refer to the numbers.有一个 case 语句来映射名称,但从那时起您可以使用名称而无需引用数字。

like so:像这样:

init() {
    self = .Cool
}

In Swift 5.1 it's now possible to set default values.在 Swift 5.1 中,现在可以设置默认值。 Your code would look like this:您的代码如下所示:

enum PersonType {
  case cool(String = "cool")
  case nice(String = "rude")
  case soLazy(String = "so-lazy")
}

This question is pretty old now and a lot has moved on in the Swift world.这个问题现在已经很老了,而且在 Swift 世界中已经发生了很多变化。 With Swift 5 I would recommend the approach below which involves creating a new initializer for the enum:使用 Swift 5 我会推荐下面的方法,它涉及为枚举创建一个新的初始化程序:

public enum PersonType:String, ExpressibleByNilLiteral {

    case Cool = "cool"
    case Nice = "rude"
    case SoLazy = "so-lazy"

    public init(nilLiteral:()) {
        self = .SoLazy
    }

    public init!(_ optionalValue:RawValue?) {
        guard let rawValue = optionalValue,
              let validValue = PersonType(rawValue:rawValue) else {
            self = .SoLazy
            return
        }
        self = validValue
    }

    public var description: String {
        switch self {
        case .Cool return "Cool Person"
        //... etc
        }
    }

    public var typeImage: String {
        switch self {
        case .Cool return "cool.png"
        //... etc
        }
    }
}

Use it like this:像这样使用它:

self.personType = PersonType(personTypeKey)

Or like this:或者像这样:

self.personType = nil

In either case, even if the value isn't valid for the PersonType enum or it's just nil you will get a valid enum that's set to the default value of .SoLazy在任何一种情况下,即使该值对PersonType枚举无效或者它只是 nil,您也会获得一个有效的枚举,该枚举设置为 .SoLazy 的默认值

This is similar to several other approaches in this thread but instead of listing out all the possible valid values (which can be unwieldy if there are a lot of them) it uses a multiple guard let = statement to guarantee it works.这与该线程中的其他几种方法类似,但它没有列出所有可能的有效值(如果有很多可能会很笨拙),它使用多重guard let =语句来保证它有效。

Try this approach.试试这个方法。

public enum PersonType:String {

    case Cool                       = "cool"
    case Nice                       = "rude"
    case SoLazy                     = "so-lazy"

    static let allKeys = [Cool.rawValue, Nice.rawValue, SoLazy.rawValue]
}

extension PersonType
{
    func description(personTypeKey : String) -> String {

        if PersonType.allKeys.contains(personTypeKey)
        {
            switch self {
            case .Cool:
                return "Cool person"
            case .Nice:
                return "Nice person"
            case .SoLazy:
                return "its so lazy person"
            }
        }
        else
        {
            return "YourTextHere"
        }
    }

    func typeImage(personTypeKey : String) -> String {

        if PersonType.allKeys.contains(personTypeKey)
        {
            switch self {
            case .Cool:
                return "cool.png"
            case .Nice:
                return "img_nice.png"
            case .SoLazy:
                return "lazy.png"
            }
        }
        else
        {
            return "YourImageHere"
        }
    }
}

For you case:对于你的情况:

Default Value of Enum : I just add an default computed property, Or include an customize init. Enum 的默认值:我只是添加了一个default计算属性,或者包含一个自定义的 init。

public enum PersonType:String {

    case Cool                       = "cool"
    case Nice                       = "rude"
    case SoLazy                     = "so-lazy"

    /// add a `default` computer property
    public static var `default`: PersonType {
        return .SoLazy
    }

    /// add an customize init function 
    public init(person: String? = nil) {
        if let person = person {
            switch person {
            case "cool": self = .Cool
            case "rude": self = .Nice
            case "so-lazy": self = .SoLazy
            default: self = .SoLazy
            }
        } else {
            self = .SoLazy
        }
    }

    public var description: String {
        switch self {
        case .Cool:
            return "Cool person"
        case .Nice:
            return "Nice person"
        case .SoLazy:
            return "its so lazy person"
        }
    }

    public var typeImage: String {
        switch self {
        case .Cool:
            return "cool.png"
        case .Nice:
            return "img_nice.png"
        case .SoLazy:
            return "lazy.png"
        }
    }

}

To use:使用:

if let personType = PersonType(rawValue:personTypeKey ?? "") {
    self.personType = personType
} else {
    self.personType = PersonType.default
}

Or或者

if let personType = PersonType(rawValue:personTypeKey ?? "") {
    self.personType = personType
} else {
    self.personType = PersonType()
}

Default Value of Enum With Associated Value:带有关联值的枚举的默认值:

public enum Gender {
    case man
    case woman
}

public enum PersonType {

    case cool(Gender)
    case nice(Gender)
    case soLazy(Gender)

    public static var `default`: PersonType {
        return PersonType.make.soLazy()
    }

    public enum Builder {
        public static func cool() -> PersonType {
            return PersonType.cool(.woman)
        }
        public static func nice() -> PersonType {
            return PersonType.nice(.woman)
        }
        public static func soLazy() -> PersonType {
            return PersonType.soLazy(.woman)
        }
    }

    public static var make: PersonType.Builder.Type {
        return PersonType.Builder.self
    }


    public var description: String {
        switch self {
        case .cool(let gender):
            switch gender {
            case .man: return "Cool boy"
            case .woman: return "Cool girl"
            }
        case .nice(let gender):
            switch gender {
            case .man: return "Nice boy"
            case .woman: return "Nice girl"
            }
        case .soLazy(let gender):
            switch gender {
            case .man: return "its so lazy boy"
            case .woman: return "its so lazy girl"
            }
        }
    }

    public var typeImage: String {
        switch self {
        case .cool(_):
            return "cool.png"
        case .nice(_):
            return "img_nice.png"
        case .soLazy(_):
            return "lazy.png"
        }
    }

}

To use:使用:

let onePersonType = PersonType.default
let anotherPersonType = PersonType.make.soLazy()

The second case solution I was found on Ilya Puchka' blog .我在Ilya Puchka 的博客上找到了第二个案例解决方案。 And also it's mentioned in swift's proposal .并且在swift 的提案中也提到了这一点。

To answer your question:回答你的问题:

public enum PersonType:String {

    case Cool                       = "cool"
    case Nice                       = "rude"
    case SoLazy                     = "so-lazy"
    static var `default`: PersonType { return .SoLazy }

    public init(rawValue: RawValue) {
        switch rawValue {
        case PersonType.Cool.rawValue: self = .Cool
        case PersonType.Nice.rawValue: self = .Nice
        case PersonType.SoLazy.rawValue: self = .SoLazy
        default: self = .default
        }
    }

    public var description: String {
        switch self {
        case .Cool:
            return "Cool person"
        case .Nice:
            return "Nice person"
        case .SoLazy:
            return "its so lazy person"
        }
    }

    public var typeImage: String {
        switch self {
        case .Cool:
            return "cool.png"
        case .Nice:
            return "img_nice.png"
        case .SoLazy:
            return "lazy.png"
        }
    }

}

Now since having no failable initializer with default value replace your:现在因为没有带有默认值的可失败初始化器替换你的:

if let personType = PersonType(rawValue:personTypeKey ?? "") {
   self.personType = personType
}

With:与:

personType = PersonType(rawValue: personTypeKey)

I recommend using such an approach我建议使用这种方法

public enum Result {
    case passed(hint: String)
    case failed(message: String)

    static let passed: Self = .passed(hint: "")
}


let res: Result = Result.passed

I wonder if dictionary is not a better fit than enum here:我想知道字典是否比 enum 更合适:

let dict = [
    "Cool": "cool",
    "Nice": "rude",
    "SoLazy": "so-lazy"
]

let personType = "unknown"
let personDescription = dict[personType] ?? "Unknown"

Less typing, faster processing, more natural handling of the default case, easier to expand.更少的输入,更快的处理,更自然的默认情况处理,更​​容易扩展。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM