[英]How can I extend a Swift protocol with a default parameter when using generic types?
[英]How can I handle different types using generic type in swift?
我正在尝试编写一个类,使我可以轻松在两个值之间进行插值。
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
}
}
不幸的是,它在return returnRect
时给了我一个错误:无法将'CGRect'类型的返回表达式转换为'T'类型。 也许我不了解泛型的用法...我只是想拥有一个可以处理各种类型之间的插值的函数,而不是拥有像func interpolate(from: Int, to: Int)
, func interpolate(from: CGPoint, to: CGPoint)
,等等。
如果您使用Protocol
来扩展通用类型,那将是很好的。
protocol Interpolate {
associatedtype T
static func interpolate(from: T, to: T, progress: CGFloat) -> T
}
然后让CGRect
扩展符合您的协议:
extension CGRect: Interpolate {
typealias T = CGRect
static func interpolate(from: T, to: T, progress: CGFloat) -> CGRect.T {
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
}
}
var from = CGRect(x: 0, y: 0, width: 1, height: 1) // (0, 0, 1, 1)
var to = CGRect(x: 1, y: 1, width: 0, height: 0) // (1, 1, 0, 0)
CGRect.interpolate(from, to: to, progress: 1) // (1, 1, 0, 0)
此外,这将使NSString
轻松符合协议Interpolate
,例如:
extension NSString: Interpolate {
typealias T = NSString
static func interpolate(from: T, to: T, progress: CGFloat) -> NSString.T {
//...
return ""
}
}
问题是T
是一个通用占位符-这意味着您无法从函数中知道T
的实际具体类型是什么。 因此,尽管你可以有条件地投from
和to
到CGRect
(从而建立该T == CGRect
),斯威夫特是无法推断出这一信息,因此禁止试图返回一个CGRect
时,预计的回报T
。
因此,粗略的解决方案是将返回结果强制转换回T
,以弥合这种信息与类型系统的差距:
if let from = from as? CGRect, let to = to as? CGRect {
// ...
return returnRect as! T
}
但是,这种类型转换实际上表明您正在与类型系统作斗争,没有利用泛型提供的静态类型,因此不建议这样做。
正如@Wongzigii已经说过的 ,更好的解决方案是使用协议。 例如,如果您定义了他在其答案中显示的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在他对其他问题的回答中建议的解决方案-重载运算符以实现此功能。 与以前的解决方案一样,关键是要定义一个协议,该协议封装您要在每种类型上定义的功能,然后将通用函数限制为该协议。
一个简单的实现可能看起来像这样:
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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.