问题描述
class插值
(
class func interpolate< T>(from:T,to:T,progress:CGFloat) - > T
{
// Safety
assert(进度> = 0&& progress< = 1,进度值无效:\(进度))
如果让a = from as? CGFloat,让b =为as? CGFloat
{
}
如果让a = from as? CGPoint,让b =为as? CGPoint
{
}
如果let = from as? CGRect,让=去as? CGRect
{
var returnRect = CGRect()
returnRect.origin.x = from.origin.x +(to.origin.x-from.origin.x)* progress
returnRect.origin.y = from.origin.y +(to.origin.y-from.origin.y)* progress
returnRect.size.width = from.size.width +(to.size.width -from.size.width)* progress
returnRect.size.height = from.size.height +(to.size.height-from.size.height)* progress
return returnRect //不能转换返回类型'CGRect'的表达式为返回类型'T'
}
返回来自
}
}
不幸的是,它在 return returnRect 中给我一个错误:无法转换'CGRect'返回类型'T'。也许我不理解泛型是如何使用的......我只想有一个函数可以处理各种类型的插值,而不是像 func interpolate(from:Int,to :Int), func interpolate(from:CGPoint,to:CGPoint)等。
因此,粗略的解决方案是强制投射回报结果回到 T ,以便弥补信息与类型系统的差距:
if let from = from as? CGRect,让=去as? CGRect {
// ...
返回returnRect as! T
}
然而,这种类型演绎真的是一个标志,重新对抗类型系统,而不是利用泛型提供的静态类型,因此不推荐。
更好的解决方案,如,就是使用一个协议。例如,如果您在他的答案中显示 Interpolate 协议,则可以使用此协议来约束您的通用占位符 T 插入函数:
class Interpolation {
class func interpolate< T:Interpolate>(from:T,to:T,progress:CGFloat) - > T {
//安全
assert(进度> = 0&& progress< = 1,无效进度值:\(进度))
返回T.interpolate(from:from,to:progress,progress)
}
}
这解决了许多问题 - 它消除了运行时类型转换,而是使用协议约束来调用专门的 interpolate $ c $功能。协议约束还可以防止在编译时传递任何不符合 Interpolate 的类型,因此也解决了在类型转换时要做什么的问题失败。
尽管如此,我确实很喜欢 @ JoshCaswell在他的回答中建议您的其他问题 - 重载运营商以实现此功能。与以前的解决方案一样,关键是定义一个协议,该协议封装了您在每种类型上定义的功能,然后将通用功能约束到此协议。
一个简单的实现可能如下所示:
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。宽度+ rhs.size.width,
高度: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,
高度:lhs.size.height * rhs)
}
扩展CGRect:可插入的{}
扩展CGFloat:可插入的{}
类插值{
class func interpolate< T:Interpolatable>(from:T,to:T,progress:CGFloat) - > T {
assert(progress> = 0&& progress< = 1,无效的进度值:\(progress))
从+返回b $ b}
}
I'm trying to write a class which allows me to easily interpolate between two values.
class Interpolation { class func interpolate<T>(from: T, to: T, progress: CGFloat) -> T { // Safety assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)") if let a = from as? CGFloat, let b = to as? CGFloat { } if let a = from as? CGPoint, let b = to as? CGPoint { } if let from = from as? CGRect, let to = to as? CGRect { var returnRect = CGRect() returnRect.origin.x = from.origin.x + (to.origin.x-from.origin.x) * progress returnRect.origin.y = from.origin.y + (to.origin.y-from.origin.y) * progress returnRect.size.width = from.size.width + (to.size.width-from.size.width) * progress returnRect.size.height = from.size.height + (to.size.height-from.size.height) * progress return returnRect // Cannot convert return expression of type 'CGRect' to return type 'T' } return from } }
Unfortunately, it gives me an error at return returnRect: Cannot convert return expression of type 'CGRect' to return type 'T'. Maybe I'm not understanding how generics are used...I just want to have one function that will handle interpolating between various types, rather than having a bunch of functions like func interpolate(from: Int, to: Int), func interpolate(from: CGPoint, to: CGPoint), etc.
The problem is that T is a generic placeholder – meaning that you cannot know what the actual concrete type of T is from within the function. Therefore although you are able to conditionally cast from and to to a CGRect (thus establishing that T == CGRect), Swift is unable to infer this information and therefore prohibits attempting to return a CGRect when it expects a return of T.
The crude solution therefore is to force cast the return result back to T in order to bridge this gap in information with the type-system:
if let from = from as? CGRect, let to = to as? CGRect { // ... return returnRect as! T }
However, this kind of type-casting is really a sign that you're fighting the type-system and not taking advantage of the static typing that generics offer, and therefore is not recommended.
The better solution, as @Wongzigii has already said, is to use a protocol. For example, if you define an Interpolate protocol as he shows in his answer – you can then use this protocol in order to constrain your generic placeholder T in your interpolate function:
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) } }
This solves many of your problems – it does away with the runtime type-casting and instead uses the protocol constraint in order to call the specialised interpolate function. The protocol constraint also prevents you from passing any types that don't conform to Interpolate at compile-time, and therefore also solves the problem of what to do when your type-casting fails.
Although that being said, I actually quite like the solution that @JoshCaswell suggested in his answer to your other question – overloading operators in order to achieve this functionality. As with the previous solution, the key is to define a protocol that encapsulates the functionality you're defining on each type, and then constrain your generic function to this protocol.
A simple implementation may look like this:
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 } }
这篇关于我怎样才能在swift中使用泛型类型处理不同的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!