问题是T
是一个通用占位符 - 这意味着你无法知道实际的具体类型是什么T
来自函数内部。因此,尽管你可以有条件地施放from
and to
to a CGRect
(从而确定T == CGRect
),Swift 无法推断出此信息,因此禁止尝试返回CGRect
当它期望回报时T
.
因此,粗略的解决方案是将返回结果强制转换回T
为了弥合信息与类型系统之间的差距:
if let from = from as? CGRect, let to = to as? CGRect {
// ...
return returnRect as! T
}
然而,这种类型转换实际上表明您正在与类型系统作斗争,并且没有利用泛型提供的静态类型,因此不建议这样做。
更好的解决方案,如@Wongzigii 已经说过了 https://stackoverflow.com/a/38653299/2976878,就是使用协议。例如,如果您定义一个Interpolate
正如他在回答中所示的协议 - 然后您可以使用此协议来约束您的通用占位符T
在你的interpolate
功能:
class Interpolation {
class func interpolate<T:Interpolate>(from: T, to: T, progress: CGFloat) -> T {
// Safety
assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)")
return T.interpolate(from: from, to: to, progress: progress)
}
}
这解决了您的许多问题 - 它消除了运行时类型转换,而是使用协议约束来调用专门的interpolate
功能。协议约束还阻止您传递任何不符合协议的类型Interpolate
在编译时,因此也解决了类型转换失败时该怎么办的问题。
尽管如此,我实际上很喜欢这个解决方案@JoshCaswell 在他的回答中建议 https://stackoverflow.com/a/38687308/2976878回答你的另一个问题——重载运算符以实现此功能。与前面的解决方案一样,关键是定义一个协议来封装您在每种类型上定义的功能,然后将通用函数限制为该协议。
一个简单的实现可能如下所示:
protocol Interpolatable {
func +(lhs:Self, rhs:Self) -> Self
func -(lhs:Self, rhs:Self) -> Self
func *(lhs:Self, rhs:CGFloat) -> Self
}
func +(lhs:CGRect, rhs:CGRect) -> CGRect {
return CGRect(x: lhs.origin.x+rhs.origin.x,
y: lhs.origin.y+rhs.origin.y,
width: lhs.size.width+rhs.size.width,
height: lhs.size.height+rhs.size.height)
}
func -(lhs:CGRect, rhs:CGRect) -> CGRect {
return CGRect(x: lhs.origin.x-rhs.origin.x,
y: lhs.origin.y-rhs.origin.y,
width: lhs.size.width-rhs.size.width,
height: lhs.size.height-rhs.size.height)
}
func *(lhs:CGRect, rhs:CGFloat) -> CGRect {
return CGRect(x: lhs.origin.x*rhs,
y: lhs.origin.y*rhs,
width: lhs.size.width*rhs,
height: lhs.size.height*rhs)
}
extension CGRect : Interpolatable {}
extension CGFloat : Interpolatable {}
class Interpolation {
class func interpolate<T:Interpolatable>(from: T, to: T, progress: CGFloat) -> T {
assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)")
return from + (to - from) * progress
}
}