Swift 带有枚举的协议扩展
Swift protocol extensions with enums
所以我有几个代表各种单位系统的枚举:
enum MassUnit:Double{
case Pound = 453.59237, Ounce = 28.349523125, Gram = 1.0, Kilogram = 1000.0;
}
enum VolumeUnit:Double{
case Teaspoon = 1, Tablespoon = 3, Cup = 48, Pint = 96, Quart = 192, Gallon = 768, Liter = 202.884136211, Milliliter = 0.202884136211
}
enum TimeUnit:Double{
case Second = 1, Minute = 60, Hour = 3600, Day = 86400, Week = 604800, Year = 31536000
}
我想做的是能够将一个单位转换为另一个单位,例如从年转换为秒。为此,我确保我的枚举的原始值对应于转换乘数,例如 1 分钟 = 60 秒。因此,给定某个单位的 x 数量,转换就是
x * rawValue1 / rawValue2 // rawValue2 = rawValue of desired unit.
虽然这种转换很简单,但我希望提高效率并使用协议:
protocol Convertable{
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
然后,我可以扩展枚举:
extension TimeUnit:Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: TimeUnit) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;
}
}
然后我可以像这样简单地转换:
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second)
// returns 80101440
这很好,但是,根据我要转换的单位数量,可能会有很多重复的相同代码。
所以,我想做的是以某种方式使用协议扩展。
extension Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: Self) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;// Compile error...
}
}
这就是我遇到麻烦的地方,输出单元被声明为 self,它对 rawValue 一无所知。
有什么想法吗?
就在我问这个问题的时候,我得到了答案:让 Convertible 协议也需要一个 rawValue 变量:
protocol Convertable{
var rawValue:Double{get}
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
这样,引用 self.rawValue
是已知的,并且由于枚举已经有一个 rawValue,不需要额外的工作:
enum MassUnit:Double, Convertable{
case Pound = 453.59237, Ounce = 28.349523125, Gram = 1.0, Kilogram = 1000.0;
}
enum VolumeUnit:Double, Convertable{
case Teaspoon = 1, Tablespoon = 3, Cup = 48, Pint = 96, Quart = 192, Gallon = 768, Liter = 202.884136211, Milliliter = 0.202884136211
}
enum TimeUnit:Double, Convertable{
case Second = 1, Minute = 60, Hour = 3600, Day = 86400, Week = 604800, Year = 31536000
}
然后使用转换器:
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second) // 80101440
MassUnit.Gram.convert(inputAmount: 20.0, outputUnit: .Ounce) //0.70547
VolumeUnit.Pint.convert(inputAmount: 0.2, outputUnit: .Tablespoon)// 6.4000
更好的是,在转换不能像这样工作的奇怪情况下,我可以用我自己的实现覆盖转换函数,例如温度:
enum TemperatureUnit:Double, Convertable{
case Celsius, Fahrenheit
func convert(inputAmount inputAmount: Double, outputUnit: TemperatureUnit) -> Double {
if self == outputUnit {
return inputAmount;
} else if self == .Celsius {
return inputAmount * 9.0/5.0 + 32.0
} else {
return (inputAmount - 32.0) * 5.0 / 9.0;
}
}
}
TemperatureUnit.Celsius.convert(inputAmount: 3, outputUnit: .Fahrenheit) // 37.4
TemperatureUnit.Fahrenheit.convert(inputAmount: 0, outputUnit: .Celsius) // -17.7778
漂亮!
所以我有几个代表各种单位系统的枚举:
enum MassUnit:Double{
case Pound = 453.59237, Ounce = 28.349523125, Gram = 1.0, Kilogram = 1000.0;
}
enum VolumeUnit:Double{
case Teaspoon = 1, Tablespoon = 3, Cup = 48, Pint = 96, Quart = 192, Gallon = 768, Liter = 202.884136211, Milliliter = 0.202884136211
}
enum TimeUnit:Double{
case Second = 1, Minute = 60, Hour = 3600, Day = 86400, Week = 604800, Year = 31536000
}
我想做的是能够将一个单位转换为另一个单位,例如从年转换为秒。为此,我确保我的枚举的原始值对应于转换乘数,例如 1 分钟 = 60 秒。因此,给定某个单位的 x 数量,转换就是
x * rawValue1 / rawValue2 // rawValue2 = rawValue of desired unit.
虽然这种转换很简单,但我希望提高效率并使用协议:
protocol Convertable{
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
然后,我可以扩展枚举:
extension TimeUnit:Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: TimeUnit) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;
}
}
然后我可以像这样简单地转换:
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second)
// returns 80101440
这很好,但是,根据我要转换的单位数量,可能会有很多重复的相同代码。
所以,我想做的是以某种方式使用协议扩展。
extension Convertable{
func convert(inputAmount inputAmount: Double, outputUnit: Self) -> Double {
return inputAmount * self.rawValue / outputUnit.rawValue;// Compile error...
}
}
这就是我遇到麻烦的地方,输出单元被声明为 self,它对 rawValue 一无所知。
有什么想法吗?
就在我问这个问题的时候,我得到了答案:让 Convertible 协议也需要一个 rawValue 变量:
protocol Convertable{
var rawValue:Double{get}
func convert(inputAmount inputAmount:Double, outputUnit:Self)->Double;
}
这样,引用 self.rawValue
是已知的,并且由于枚举已经有一个 rawValue,不需要额外的工作:
enum MassUnit:Double, Convertable{
case Pound = 453.59237, Ounce = 28.349523125, Gram = 1.0, Kilogram = 1000.0;
}
enum VolumeUnit:Double, Convertable{
case Teaspoon = 1, Tablespoon = 3, Cup = 48, Pint = 96, Quart = 192, Gallon = 768, Liter = 202.884136211, Milliliter = 0.202884136211
}
enum TimeUnit:Double, Convertable{
case Second = 1, Minute = 60, Hour = 3600, Day = 86400, Week = 604800, Year = 31536000
}
然后使用转换器:
TimeUnit.Year.convert(inputAmount: 2.54, outputUnit: .Second) // 80101440
MassUnit.Gram.convert(inputAmount: 20.0, outputUnit: .Ounce) //0.70547
VolumeUnit.Pint.convert(inputAmount: 0.2, outputUnit: .Tablespoon)// 6.4000
更好的是,在转换不能像这样工作的奇怪情况下,我可以用我自己的实现覆盖转换函数,例如温度:
enum TemperatureUnit:Double, Convertable{
case Celsius, Fahrenheit
func convert(inputAmount inputAmount: Double, outputUnit: TemperatureUnit) -> Double {
if self == outputUnit {
return inputAmount;
} else if self == .Celsius {
return inputAmount * 9.0/5.0 + 32.0
} else {
return (inputAmount - 32.0) * 5.0 / 9.0;
}
}
}
TemperatureUnit.Celsius.convert(inputAmount: 3, outputUnit: .Fahrenheit) // 37.4
TemperatureUnit.Fahrenheit.convert(inputAmount: 0, outputUnit: .Celsius) // -17.7778
漂亮!