如何在方法参数中遵守具有原始类型的协议方法? [英] How to adhere to protocol method with a Raw type in method argument?

查看:61
本文介绍了如何在方法参数中遵守具有原始类型的协议方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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 类型的枚举一起使用?

How can I make Mass adhere correctly to Measurement? What change in the Measurement protocol is needed to get it to work with enum of type String?

问题更新了更多关于为什么转换方法签名应该说明给定参数的信息.该代码是我正在构建的名为 Indus Valley

Question updated with more information about why the convert method signature should say something about the argument given. The code is part of an open source Unit framework i'm building called Indus Valley

推荐答案

在处理单位转换时,我建议在转换时不要尝试使用 Strings 来表示单位得到了上面的设置.这将使您的代码在每次要进行转换时检查 String 是否可以转换为其各自的枚举变得复杂.另外,如果您想使用 MassUnit/VolumeUnit 而不是 String 怎么办?

When dealing with your unit conversions, I advise not trying to use Strings to represent the units when converting the way you've got setup above. That's going to make your code complicated with checking the String can be converted to its respective enum every time you want to make a conversion. Also, what if you want to use the a MassUnit/VolumeUnit instead of a String?

我建议使用类似于我在下面概述的设置.它引用了我之前的答案 - 如何在 Swift 中表示质量的大小?

I would recommend using the a setup similar to what I've outlined below. It references my previous answer - How to represent magnitude for mass in Swift?

(注意 - 我已经排除了与体积有关的任何事情,因为它与质量的实现基本相同)

(Note - I've excluded anything to do with the volume because it's basically the same as the implementation for the mass)

我会像这样制作单位:

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
}

然后我会像这样制作质量/体积:

Then I'd make the masses/volumes like so:

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 的每种类型重写代码.

Now for the converting function! Using a global function means you don't need to rewrite the code for every type than conforms to 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
}

然后你可以像这样使用前面的代码:

You can then use the previous code like so:

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

这篇关于如何在方法参数中遵守具有原始类型的协议方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆