如何在方法参数中使用Rawtypes遵守协议方法?
protocol Measurement { mutating func convert(#toUnit: String) } enum MassUnit : String { case Milligram = "mg" } enum VolumeUnit : String { case Milliliter = "ml" } struct Mass : Measurement { mutating func convert(#toUnit: MassUnit) // Build error: Does not adhere to 'Measurement' } struct Volume : Measurement { mutating func convert(#toUnit: VolumeUnit) // Build error: Does not adhere to 'Measurement' } func +<T: Measurement> (left:T, right:T) -> Measurement { let newRightValue = right.convert(toUnit: left.unit) return T(quantity: left.quantity + newRightValue.quantity , unit: left.unit) }
我如何让Mass
能够正确地遵循Measurement
? 需要在Measurement
协议中做什么改变才能使用String
types的枚举?
问题更新了更多的信息,为什么转换方法签名应该说一些关于给出的论点。 这个代码是我正在build设的一个叫做Indus Valley的开源单元框架的一部分
在处理你的单位转换时,我build议不要在使用上面设置的方式转换时使用String
来表示单位。 这将使你的代码变得复杂,检查String
可以转换为各自的枚举,每次你想进行转换。 另外,如果你想使用一个MassUnit
/ VolumeUnit
而不是一个String
呢?
我会build议使用类似于我下面概述的设置。 它引用了我以前的答案 – 如何在Swift中表示质量的大小?
(注意 – 我已经排除了与音量有关的任何事情,因为它与质量的实现基本相同)
我会这样做的单位:
protocol UnitProtocol { var magnitude: Int { get } init?(rawValue: String) } // Taken from my previous answer. enum MassUnit: String, UnitProtocol, Printable { case Milligram = "mg" case Gram = "g" var magnitude: Int { let mag: Int switch self { case .Milligram: mag = -3 case .Gram : mag = 0 } return mag } var description: String { return rawValue } } // Not making this a method requirement of `UnitProtocol` means you've only got to // write the code once, here, instead of in every enum that conforms to `UnitProtocol`. func ordersOfMagnitudeFrom<T: UnitProtocol>(unit1: T, to unit2: T) -> Int { return unit1.magnitude - unit2.magnitude }
那么我会这样做群众/卷:
protocol UnitConstruct { typealias UnitType: UnitProtocol var amount: Double { get } var unit : UnitType { get } init(amount: Double, unit: UnitType) } struct Mass : UnitConstruct { let amount: Double let unit : MassUnit }
现在的转换function! 使用全局函数意味着您不需要重写每个types的代码,而不是符合UnitConstruct
。
func convert<T: UnitConstruct>(lhs: T, toUnits unit: T.UnitType) -> T { let x = Double(ordersOfMagnitudeFrom(lhs.unit, to: unit)) return T(amount: lhs.amount * pow(10, x), unit: unit) } // This function is for converting to different units using a `String`, // as asked in the OP. func convert<T: UnitConstruct>(lhs: T, toUnits unit: String) -> T? { if let unit = T.UnitType(rawValue: unit) { return convert(lhs, toUnits: unit) } return nil }
您可以使用之前的代码,如下所示:
let mass1 = Mass(amount: 1.0, unit: .Gram) let mass2 = convert(mass1, toUnits: .Milligram) // 1000.0 mg // Or, converting using Strings: let right = convert(mass1, toUnits: "mg") // Optional(1000.0 mg) let wrong = convert(mass1, toUnits: "NotAUnit") // nil
您可能会混淆enum MassUnit : String
与inheritance。
与class ChildClass : ParentClass
表示ChildClass
inheritance自ParentClass
相反, enum MassUnit : String
具有稍微不同的含义,告诉枚举的rawType是String
,而不是枚举inheritanceStringtypes。
所以MassUnit
不是Stringtypes的。 你的MassUnit
的rawValue
是Stringtypes的,但是为了访问你需要调用enum的rawValue
属性来获得这个String
等价物。
因此, mutating func convert(#toUnit: String)
和mutating func convert(#toUnit: MassType)
不兼容,因为MassType
本身不是String
。 只有它的rawValue
是。