如何投射通用数字类型' T'到CGFloat

时间:2016-09-14 08:57:04

标签: swift generics type-conversion

我有一个通过泛型T类型接受数字类型的类,我希望能够将它转换为CGFloat,但它会抛出:

  

无法为类型' CGFloat'调用初始化程序。使用类型'(T)'

的参数列表

在课堂上我必须做些什么才能成功转换它?

CGFloat(self.top) - 这就是它不喜欢

' T' type的定义如下:

protocol Numeric:Comparable, Equatable {
    //
    init(_ v:Float)
    init(_ v:Double)
    init(_ v:Int)
    init(_ v:UInt)
    init(_ v:Int8)
    init(_ v:UInt8)
    init(_ v:Int16)
    init(_ v:UInt16)
    init(_ v:Int32)
    init(_ v:UInt32)
    init(_ v:Int64)
    init(_ v:UInt64)
}
extension Float: Numeric {}
extension Double: Numeric {}
extension Int: Numeric {}
extension Int8: Numeric {}
extension Int16: Numeric {}
extension Int32: Numeric {}
extension Int64: Numeric {}
extension UInt: Numeric {}
extension UInt8: Numeric {}
extension UInt16: Numeric {}
extension UInt32: Numeric {}
extension UInt64: Numeric {}

class MyClass<T: Numeric> {
//...
    var top:T
}

当我尝试使用as时,会弹出此运行时错误

  

无法转换类型&#39; Swift.Double&#39; (0x1002b64f8)到   &#39; CoreGraphics.CGFloat&#39; (0x1004e8740)。

2 个答案:

答案 0 :(得分:1)

解决方案在我的掌握之内:)首先看一下这个链接:

Richard Fox - Cast-Free Arithmetic in Swift

然后就像在现有代码中添加以下内容一样简单:

protocol Numeric:Comparable, Equatable {
    //
    init(_ v:Float)
    init(_ v:Double)
    init(_ v:Int)
    init(_ v:UInt)
    init(_ v:Int8)
    init(_ v:UInt8)
    init(_ v:Int16)
    init(_ v:UInt16)
    init(_ v:Int32)
    init(_ v:UInt32)
    init(_ v:Int64)
    init(_ v:UInt64)
    init(_ value: CGFloat)
}
extension Numeric {

    func convert<T: Numeric>() -> T {
        switch self {
        case let x as CGFloat:
            return T(x) //T.init(x)
        case let x as Float:
            return T(x)
        case let x as Double:
            return T(x)
        case let x as Int:
            return T(x)
        case let x as UInt:
            return T(x)
        case let x as Int8:
            return T(x)
        case let x as UInt8:
            return T(x)
        case let x as Int16:
            return T(x)
        case let x as UInt16:
            return T(x)
        case let x as Int32:
            return T(x)
        case let x as UInt32:
            return T(x)
        case let x as Int64:
            return T(x)
        case let x as UInt64:
            return T(x)
        default:
            assert(false, "Numeric convert cast failed!")
            return T(0)
        }
    }
}

extension CGFloat{
    public  init(_ value: CGFloat){
        self = value
    }
}

然后按如下方式使用它:let c:CGFloat = self.top.convert()

答案 1 :(得分:1)

作为扩展程序to my answer here,您可以通过使用“阴影方法”静态实现此目的,以便允许Numeric类型强制自己使用任何其他Numeric类型(假设目标类型的初始化程序列为协议要求。

例如,您可以像这样定义Numeric协议:

protocol Numeric : Comparable, Equatable {

    init(_ v:Float)
    init(_ v:Double)
    init(_ v:Int)
    init(_ v:UInt)
    init(_ v:Int8)
    init(_ v:UInt8)
    init(_ v:Int16)
    init(_ v:UInt16)
    init(_ v:Int32)
    init(_ v:UInt32)
    init(_ v:Int64)
    init(_ v:UInt64)
    init(_ v:CGFloat)

    // 'shadow method' that allows instances of Numeric
    // to coerce themselves to another Numeric type
    func _asOther<T:Numeric>() -> T
}

extension Numeric {

    // Default implementation of init(fromNumeric:) simply gets the inputted value
    // to coerce itself to the same type as the initialiser is called on
    // (the generic parameter T in _asOther() is inferred to be the same type as self)
    init<T:Numeric>(fromNumeric numeric: T) { self = numeric._asOther() }
}

然后将类型符合Numeric,如下所示:

// Implementations of _asOther() – they simply call the given initialisers listed
// in the protocol requirement (it's required for them to be repeated like this,
// as the compiler won't know which initialiser you're referring to otherwise)
extension Float   : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Double  : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension CGFloat : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int     : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int8    : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int16   : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int32   : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int64   : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt    : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt8   : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt16  : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt32  : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt64  : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}

使用示例:

class MyClass<T : Numeric> {

    var top : T

    init(_ top:T) {
        self.top = top
    }

    func topAsCGFloat() -> CGFloat {
        return CGFloat(fromNumeric: top)
    }
}

let m = MyClass(Int32(42))
let converted = m.topAsCGFloat()

print(type(of:converted), converted) // prints: CGFloat 42.0

此解决方案可能不比实现切换符合Numeric的每个类型的方法短 - 但是由于此解决方案不依赖于运行时类型转换,编译器可能会有更多优化机会

它还受益于静态类型检查,这意味着您不能将新类型符合Numeric而不实现将该类型转换为另一种类型Numeric的逻辑(在您的情况下,您如果在switch)中没有处理类型,则会在运行时崩溃。

此外,由于封装,扩展更灵活,因为转换类型的逻辑是在符合Numeric的每个具体类型中完成的,而不是处理可能情况的单个方法。