Swift中的枚举和泛型

时间:2018-07-11 05:55:26

标签: swift generics enums

已编辑

你好,我试图制作自己的单位转换器

但是当我尝试同时设置重量和长度时会有一些问题

重复的代码太多

enum LengthUnit: String{
    case inch
    case cm
    case m
    case yard

    static func unit(of value: String) -> LengthUnit?{
        switch value {
        case let value where value.contains("inch"):
            return .inch
        case let value where value.contains("cm"):
            return .cm
        case let value where value.contains("m"):
            return .m
        case let value where value.contains("yard"):
            return .yard
        default:
            return nil
        }
    }
}

enum WeightUnit:String {
    case g
    case kg
    case lb
    case oz

    static func unit(of value: String) -> WeightUnit?{
        switch value {
        case let value where value.contains("g"):
            return .g
        case let value where value.contains("kg"):
            return .kg
        case let value where value.contains("lb"):
            return .lb
        case let value where value.contains("oz"):
            return .oz
        default:
            return nil
        }
    }
}

不仅可以从String函数中获取单位,而且可以从许多相关的转换函数中获取代码,

所以我尝试通过泛型实现它,但我对此一无所知

如何对两种单元类型使用枚举和泛型

2 个答案:

答案 0 :(得分:2)

您可以单行执行此操作,因为此处枚举项的原始值是枚举项的字符串版本,因此LengthUnit.inch->“ inch”

extension String {
    var lengthUnit: LengthUnit? {
        get {
            return LengthUnit(rawValue:self)
        }
    }
}

更新

更新的版本包含删除字符串的数字部分的示例。最好的解决方案是什么,如果不知道期望什么样的数据就很难知道。

extension String {
    var lengthUnit: LengthUnit? {
        get {
            let string = self.trimmingCharacters(in: CharacterSet(charactersIn: "01234567890."))
           return LengthUnit(rawValue:string)
        }
    }
}

注释:由于您正在创建单位转换器,因此无论如何都需要将字符串分成值和单位才能执行转换,因此先这样做会更聪明,使用我的原始版本。

注释2:我在这里看不到如何使用泛型,您的输入始终是String,并且我看不到让函数/属性返回泛型没有任何好处。您可以只使用一个Unit枚举,然后仅拥有一个unit属性而不是两个

来简化设计

答案 1 :(得分:1)

自从String继承枚举以来,您将免费获得init?(rawValue: String)解析初始化程序。就我个人而言,我不会创建像unit(of:)这样的函数,因为它只会丢弃金额部分。相反,我将创建像parse(value: String) -> (Double, LengthUnit)?

这样的解析函数

无论如何,如果您确实希望使用unit(of:)函数并希望尽可能减少代码重复,则确实可以从使用泛型中受益。

首先,我们需要像这样的Unit标记协议

protocol UnitProtocol { }

然后,我们可以创建泛型函数,该泛型函数将使用init?(rawValue: String) RawRepresentable中的Unit来基于传递的字符串返回单位

func getUnit<U: UnitProtocol & RawRepresentable>(of value: String) -> U? where U.RawValue == String {
    // you need better function to split amount and unit parts
    // current allows expressions like "15.6.7.1cm"
    // but that's question for another topic
    let digitsAndDot = CharacterSet(charactersIn: "0123456789.")
    let unitPart = String(value.drop(while: { digitsAndDot.contains($0.unicodeScalars.first!) }))
    return U.init(rawValue: unitPart)
}

基本上就是这样。如果您不喜欢使用函数,而更喜欢静态方法,那么只需添加这些方法并在内部调用getUnit(of:)

enum LengthUnit: String, UnitProtocol {
    case inch
    case cm
    case m
    case yard

    static func unit(of value: String) -> LengthUnit? {
        return getUnit(of: value)
    }
}

enum WeightUnit: String, UnitProtocol {
    case g
    case kg
    case lb
    case oz

    static func unit(of value: String) -> WeightUnit? {
        return getUnit(of: value)
    }
}

或者,相反,我们甚至可以在各处添加unit(of:)方法甚至做得更好,并添加扩展名

extension UnitProtocol where Self: RawRepresentable, Self.RawValue == String {
    static func unit(of value: String) -> Self? {
        return getUnit(of: value)
    }
}

现在,您只需向unit(of:)String添加符合性,即可免费获得静态Unit

enum WeightUnit: String, UnitProtocol {
    case g
    case kg
    case lb
    case oz
}