我有一个通过泛型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)。
答案 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
的每个具体类型中完成的,而不是处理可能情况的单个方法。