在Swift中扩展所有数字类型

9
假设我有以下内容:
extension NSNumber{
    func toLocalCurrency(fractDigits:Int = 2)->String{
        let formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
        let userSettings:UserInfo? = UserInfo.first(sortDescriptors: nil, context: AERecord.defaultContext) as? UserInfo
        if let code = userSettings?.currency.name_short {
            formatter.currencyCode = code
        }
        formatter.maximumFractionDigits = fractDigits
        return formatter.stringFromNumber(self)!
    }
    func toLocalCurrencyWithoutFractionDigits()->String{
        return self.toLocalCurrency(fractDigits: 0)
    }
}

我希望能够支持尽可能多的Swift/Mac数字类型,例如CGFloat NSNumber Int Float等。但我不想重复自己(复制粘贴并扩展所有内容)或者在使用该函数时进行强制转换。
我尝试扩展像FloatLiteralType/Convertible这样的协议,但仍需要进行强制转换。更方便地扩展基本类型“应该”是可能的...
我也考虑过全局函数,但它们不太容易发现,感觉更像是一种hack。
在Swift中有没有好的方法来实现这个需求?

1
你可以将其作为协议并创建协议扩展来完成。有一个名为“面向协议编程”的wwdc视频将展示这一点。 - Fogmeister
那我将不得不使用Swift 2来进行默认协议实现并手动扩展每种类型。现在我正在使用Swift 1。 - Harper04
可能涉及到使用任意对象参数的函数的问题 - Michael
2
@mika,我不同意,通常使用AnyObject的更好的替代方案。 - ABakerSmith
3个回答

11
< p>正如< code>Sogmeister所说,为了解决您的问题,您需要使用Swift 2.0。

然后您可以像这样操作:

// the solution right now is to implement it twice, I'll explain why
extension IntegerType {

    func toLocalCurrency(fractDigits:Int = 2) -> String {

        let formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle

        /* ... */

        formatter.maximumFractionDigits = fractDigits
        return formatter.stringFromNumber(self as! NSNumber)! // probably like this
    }

    func toLocalCurrencyWithoutFractionDigits() -> String {

        return self.toLocalCurrency(0)
    }
}

extension FloatingPointType {
    // second implementation goes here
}

// some example
let someUInt = UInt(12340)

someUInt.toLocalCurrency() // returns "12.340,00 €" for me

更新的答案:

基本思想是扩展MyProtocol,使用默认实现函数,然后扩展IntegerTypeFloatingPointType。但这在Swift 2.0中不会发生(看这里)。原因是在这里。这里有另一个解决方案,比我之前的方案更好。

protocol MyProtocol {}

extension MyProtocol {

    func toLocalCurrency(fractDigits:Int = 2) -> String {

        let formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle

        /* ... */

        formatter.maximumFractionDigits = fractDigits
        guard let newNumber = self as? NSNumber else { fatalError("this type is not convertable to NSNumber") }
        return formatter.stringFromNumber(newNumber)!
    }

    func toLocalCurrencyWithoutFractionDigits() -> String {

        return self.toLocalCurrency(0)
    }
}

/* extend your number types you need */
extension Int : MyProtocol {} 
extension Double : MyProtocol {} // done

1
谢谢,看起来这是我们能得到的最接近的(我不喜欢 Swift 中没有根数类型),但我必须等到所有库都更新到 Swift 2.0。 - Harper04

9
我们可以创建一个协议,通过默认实现扩展它,并使所有数字类型符合该协议:
protocol FormattableNumeric {}
extension FormattableNumeric {
    var localized: String {
        guard let number = self as? NSNumber else { return "NaN" }
        return number.description(withLocale: Locale.current)
    }
}
extension Int: FormattableNumeric {}
extension UInt: FormattableNumeric {}
extension Float: FormattableNumeric {}
extension Double: FormattableNumeric {}
// etc.

根据当前的语言环境,您现在可以按照以下方式获取格式化的数字:

1000.localized // "1,000"
12_345_678.localized // "12,345,678"
(1_000_000 * Double.pi).localized // "3,141,592.65358979"

当然,为了更好地控制格式,我们也可以在实现中使用NumberFormatter

return NumberFormatter.localizedString(from: number, number: .decimal)

1

详情

  • Xcode 10.1 (10B61)
  • Swift 4.2

解决方案

extension NSNumber {
    func toLocalCurrency(fractDigits: Int = 2) -> String? {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.maximumFractionDigits = fractDigits
        return formatter.string(from: self)
    }
    func toLocalCurrencyWithoutFractionDigits() -> String? {
        return toLocalCurrency(fractDigits: 0)
    }
}

extension Numeric {
    func toLocalCurrency(fractDigits: Int = 2) -> String? {
        return (self as? NSNumber)?.toLocalCurrency(fractDigits: fractDigits)
    }
    func toLocalCurrencyWithoutFractionDigits() -> String? {
        return toLocalCurrency(fractDigits: 0)
    }
}

示例

let value = 34.234

func test<T: Numeric>(value: T) {
    let toLocalCurrency = value.toLocalCurrency()
    let result = toLocalCurrency != nil ? "\(toLocalCurrency!)" : "nil"
    print(" type: \(type(of: value)), toLocalCurrency: \(result)")
}

func test<T: NSNumber>(value: T) {
    let toLocalCurrency = value.toLocalCurrency()
    let result = toLocalCurrency != nil ? "\(toLocalCurrency!)" : "nil"
    print(" type: \(type(of: value)), toLocalCurrency: \(result)")
}

test(value: Int8(value))
test(value: Int16(value))
test(value: Int32(value))
test(value: Int(value))
test(value: Float(value))
test(value: Int16(value))
test(value: Int32(value))
test(value: Int8(value))
test(value: Double(value))
test(value: CGFloat(value))
test(value: NSNumber(value: value))

结果

enter image description here


这是关于编程的内容,请将其翻译成中文。只需返回翻译后的文本即可,无需截图。 - Josh Lee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接