已编辑
你好,我试图制作自己的单位转换器
但是当我尝试同时设置重量和长度时会有一些问题
重复的代码太多
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
函数中获取单位,而且可以从许多相关的转换函数中获取代码,
所以我尝试通过泛型实现它,但我对此一无所知
如何对两种单元类型使用枚举和泛型
答案 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
}