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
类型的枚举一起使用?
更新问题,了解有关转换方法签名为什么应该说明给定参数的更多信息。该代码是我正在建立的名为Indus Valley
的开源单元框架的一部分答案 0 :(得分:2)
在处理您的单位转换时,我建议您在转换上述设置时不要尝试使用String
代表单位。这会使您的代码变得复杂,每次要进行转换时,都可以将String
转换为其各自的枚举。另外,如果您想使用MassUnit
/ VolumeUnit
代替String
,该怎么办?
我建议使用类似于我在下面概述的设置。它引用了我以前的答案 - How to represent magnitude for mass in 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
}
现在转换功能!使用全局函数意味着您不需要为符合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
答案 1 :(得分:2)
您可能会将enum MassUnit : String
与继承混淆。
与表示class ChildClass : ParentClass
继承自ChildClass
的{{1}}相反,ParentClass
的含义略有不同,告诉枚举的rawType 是enum MassUnit : String
,而不是枚举继承了String类型。
所以String
不是String类型。您MassUnit
的{{1}}类型为String,但要访问它,您需要调用枚举的MassUnit
属性以获得rawValue
等效项。
因此,rawValue
和String
不兼容,因为mutating func convert(#toUnit: String)
本身不是mutating func convert(#toUnit: MassType)
。只有MassType
。