首页 > 解决方案 > Swift 中的枚举和泛型

问题描述

已编辑

您好,我正在尝试制作自己的单位转换器

但是当我尝试同时制作重量和长度时出现了一些问题

有很多重复的代码

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函数中获取单位,而且还有许多相关的函数进行转换,有重复的代码

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

如何为这两种 Unit 类型使用枚举和泛型

标签: swiftgenericsenums

解决方案


由于您从您那里继承了您的枚举,因此您将免费String获得init?(rawValue: String)解析初始化程序。就个人而言,我不会创建类似的函数,unit(of:)因为它只是丢弃了数量部分。相反,我会创建解析函数,如parse(value: String) -> (Double, LengthUnit)?

无论如何,如果您真的想要unit(of:)功能并希望尽可能减少代码重复,您可能确实会从使用泛型中受益。

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

protocol UnitProtocol { }

然后,我们可以创建通用函数,该函数将使用init?(rawValue: String)sRawRepresentable 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:)的一致性即可免费获得静态StringUnit

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

推荐阅读