带有枚举的Swift协议扩展

带有枚举的Swift协议扩展,swift,enums,protocols,Swift,Enums,Protocols,因此,我有几个代表各种单元系统的枚举: 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.8

因此,我有几个代表各种单元系统的枚举:

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一无所知


有什么想法吗?

当我问这个问题时,我得到了答案:可转换协议是否也需要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

漂亮

为什么不让
可转换
符合
图纸的可表示性
?i、 e.
protocol-convertable:RawRepresentable
@jjatie您还需要将其约束为
RawRepresentable
,其
RawValue
Double
protocol-convertable:RawRepresentable,其中RawValue==Double{}
应该可以工作。然而
RawRepresentable
方法的一个缺点是,与@Jbryson的解决方案相比,这意味着
Convertible
协议现在有相关的类型要求(因为
RawValue
),因此它只能通用,这可能是一个难题。