Swift 中的枚举和泛型

Don*_*Lee 2 generics enums 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不仅从String函数中获取单位,而且还有许多相关的函数进行转换,有重复的代码

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

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

Max*_*sov 5

由于您从String您那里继承了您的枚举,因此您可以init?(rawValue: String)免费获得解析初始值设定项。就个人而言,我不会创建类似的函数,unit(of:)因为它只是扔掉了金额部分。相反,我会创建像这样的解析函数parse(value: String) -> (Double, LengthUnit)?

无论如何,如果你真的想要unit(of:)函数并且想要尽可能地减少代码重复,你确实可以从使用泛型中受益。

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

protocol UnitProtocol { }
Run Code Online (Sandbox Code Playgroud)

然后,我们可以创建一个使用泛型函数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)
}
Run Code Online (Sandbox Code Playgroud)

基本上就是这样。如果你不喜欢使用函数而更喜欢静态方法,那么你只需要添加这些方法并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)
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,unit(of:)在任何地方添加方法,我们甚至可以做得更好并添加扩展

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

现在,您unit(of:)只需添加对String和的一致性即可免费获得静态Unit

enum WeightUnit: String, UnitProtocol {
    case g
    case kg
    case lb
    case oz
}
Run Code Online (Sandbox Code Playgroud)