如何在Swift中将通用数字转换为Double?

3
我创建了一个带有通用类型T的类,其中T应该只是数值型(如CGFloatDoubleInt等),并且应该能够转换为Double。我尝试过几种类型,比如NumericEquatable,但这些类型仍然包含了太多的范围。当转换为Double时,该类型应符合BinaryIntegerBinaryFloatingPoint中的任一类型,但我还没有找到一种方法让通用类型符合其中之一。
class SomeClass<T:SomeNumberTypeThatAllowsConversionToDouble> {
    var number: T
    var doubleNumber: Double
    init(number: T) {
        self.number = number
        self.doubleNumber = Double(number) //or some other function that converts from T to Double
    }
}

我不希望出现两个独立的扩展SomeClass,一个处理BinaryInteger,另一个处理BinaryFloatingPoint,因为这将违背我使用泛型的初衷。


2
你实际上想要实现什么?为什么你需要能够将任何数值类型转换为Double - undefined
“but those still cast the net too wide” 的意思是什么? - undefined
1
@M.Koot没有真正回答我的问题。如果您想创建一个通用的存储数值的容器,为什么不能使容器本身是通用的?为什么需要用相同类型来表示所有数字?您尝试解决的是什么实际问题?我问这个问题是因为对于您所询问的问题,实际上并没有一个很好的解决方案。然而,我感觉这不是您真正的问题,而是一个XY问题。因此,很可能有办法解决您的实际问题。 - undefined
@DávidPásztor 这是我正在尝试解决的问题。这是一个关于优雅的问题。你说得对,我的根本问题可以有不同的解决方式,其中之一我在问题中提到了:我可以简单地使用扩展功能。但是我觉得通用格式应该支持某种普遍的'数字'类型。当没有精度损失时,数字之间应该有一种通用/无关的转换方式。 - undefined
1
@M.Koot 很遗憾,根据当前的 Swift 类型系统,这是不可能实现的。Swift 的数字协议仍然需要一些改进,但它们被实现在目前的位置上有其合理的原因,主要是由于不同数字类型之间的低级算术差异。为您的类型创建两个通用初始化方法,一个接受 BinaryInteger,另一个接受 BinaryFloatingPoint 可能是一个解决方案,然而,很遗憾,Swift 类型系统目前无法处理这种类型约束(通用初始化方法专门针对已经是通用类型的情况)。 - undefined
显示剩余4条评论
1个回答

3

您希望实现的是将约束T遵循一组固定协议。

这与我们在Typescript中拥有的 T:number|point|size 相似(由@Dávid Pásztor提到的联合类型)。

下面是我在Swift中的解决方法。它并不那么简洁,也许我们可以有更好的解决方案。

protocol DoubleConvertible { }

extension Int:DoubleConvertible { }
extension CGFloat:DoubleConvertible { }
extension Double:DoubleConvertible {
    static func convert(_ number:DoubleConvertible) -> Double {
        if let intNumber = number as? Int {
            return Double(intNumber)
        } else if let cgFloatNumber = number as? CGFloat {
            return Double(cgFloatNumber)
        } else if let doubleNumber = number as? Double{
            return Double(doubleNumber)
        } else {
            assertionFailure()
        }
    }
}

import UIKit

class SomeClass<T> where T:DoubleConvertible {
    var number: T
    var doubleNumber: Double
    init(number: T) {
        self.number = number
        self.doubleNumber = Double.convert(number)
    }
}

print(SomeClass<Int>(number: 10).doubleNumber)
print(SomeClass<Double>(number: 10.0).doubleNumber)
print(SomeClass<CGFloat>(number: CGFloat(integerLiteral: 10)).doubleNumber)

//10.0
//10.0
//10.0

print(SomeClass<CGPoint>(number: 10).doubleNumber)
//Type 'CGPoint' does not conform to protocol 'DoubleConvertible'

1
使用is运算符对值进行类型检查,然后强制将其转换为该类型是一个非常糟糕的主意。相反,使用安全转换,例如if let int = number as? Int { return Double(int) }。在if分支中也不要进行强制转换,而是使用if分支覆盖所有支持的类型,并在默认的else分支中抛出致命错误或简单地返回默认值之后使用assertionFailure。此外,你在TypeScript中提到的被称为联合类型。 - undefined
@DávidPásztor 根据您的建议进行升级。 - undefined

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