如何将通用数字类型“T”转换为 CGFloat

Luk*_*ela 5 generics type-conversion swift

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

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

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

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

'T' 类型定义如下:

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
}
Run Code Online (Sandbox Code Playgroud)

当我尝试使用as然后这个运行时错误弹出

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

Ham*_*ish 4

作为我在这里答案的扩展,您可以通过使用“影子方法”静态地实现此目的,以便允许Numeric类型将自身强制为任何其他Numeric类型(假设目标类型的初始化程序被列为协议要求) 。

\n\n

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

\n\n
protocol Numeric : Comparable, Equatable {\n\n    init(_ v:Float)\n    init(_ v:Double)\n    init(_ v:Int)\n    init(_ v:UInt)\n    init(_ v:Int8)\n    init(_ v:UInt8)\n    init(_ v:Int16)\n    init(_ v:UInt16)\n    init(_ v:Int32)\n    init(_ v:UInt32)\n    init(_ v:Int64)\n    init(_ v:UInt64)\n    init(_ v:CGFloat)\n\n    // \'shadow method\' that allows instances of Numeric\n    // to coerce themselves to another Numeric type\n    func _asOther<T:Numeric>() -> T\n}\n\nextension Numeric {\n\n    // Default implementation of init(fromNumeric:) simply gets the inputted value\n    // to coerce itself to the same type as the initialiser is called on\n    // (the generic parameter T in _asOther() is inferred to be the same type as self)\n    init<T:Numeric>(fromNumeric numeric: T) { self = numeric._asOther() }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

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

用法示例:

\n\n
class MyClass<T : Numeric> {\n\n    var top : T\n\n    init(_ top:T) {\n        self.top = top\n    }\n\n    func topAsCGFloat() -> CGFloat {\n        return CGFloat(fromNumeric: top)\n    }\n}\n\nlet m = MyClass(Int32(42))\nlet converted = m.topAsCGFloat()\n\nprint(type(of:converted), converted) // prints: CGFloat 42.0\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

它还受益于静态类型检查,这意味着您无法Numeric在不实现将该类型转换为另一种类型的逻辑的情况下符合新类型Numeric(在您的情况下,如果该类型未在这switch)。

\n\n

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

\n