如何将通用数字类型“T"转换为 CGFloat [英] How to cast generic number type 'T' to CGFloat

查看:25
本文介绍了如何将通用数字类型“T"转换为 CGFloat的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a class that accepts numeric types through generic T type, I wanted to be able to convert it to the CGFloat but it throws:

无法使用类型为 '(T)' 的参数列表调用类型为 'CGFloat' 的初始化程序

Cannot invoke initializer for type 'CGFloat' with an argument list of type '(T)'

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

What I have to do in my class to be able to convert it successfully?

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

'T' 类型定义如下:

The 'T' type is defined as follows:

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 然后这个运行时错误弹出

When I tried with as then this runtime error popped up

无法将 'Swift.Double' (0x1002b64f8) 类型的值转换为'CoreGraphics.CGFloat' (0x1004e8740).

Could not cast value of type 'Swift.Double' (0x1002b64f8) to 'CoreGraphics.CGFloat' (0x1004e8740).

推荐答案

作为扩展我的回答,你可以通过使用影子方法"静态地实现这一点,以允许 Numeric 类型将自己强制转换为任何其他 Numeric 类型(假设目标类型的初始化程序已列出作为协议要求).

As an extension to my answer here, you could achieve this statically through using a 'shadow method' in order to allow Numeric types to coerce themselves to any other Numeric type (given that the initialiser for the destination type is listed as a protocol requirement).

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

For example, you could define your Numeric protocol like so:

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 像这样:

And then conform types to Numeric like so:

// 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 的每个类型——但是由于这个解决方案不依赖于运行时类型转换,编译器可能会有更多优化机会.

This solution is probably no shorter than implementing a method that switches through every type that conforms to Numeric – however as this solution doesn't rely on runtime type-casting, the compiler will likely have more opportunities for optimisation.

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

It also benefits from static type-checking, meaning that you cannot conform a new type to Numeric without also implementing the logic to convert that type to another type of Numeric (in your case, you would crash at runtime if the type wasn't handled in the switch).

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

Furthermore, because of encapsulation, it's more flexible to expand, as the logic to convert types is done in each individual concrete type that conforms to Numeric, rather than a single method that handles the possible cases.

这篇关于如何将通用数字类型“T"转换为 CGFloat的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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