我怎样才能在swift中使用泛型类型处理不同的类型? [英] How can I handle different types using generic type in swift?

查看:182
本文介绍了我怎样才能在swift中使用泛型类型处理不同的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  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 是一个通用的占位符 - 意味着您无法知道<$ c的实际具体类型$ c> T 来自函数内部。因此,尽管您可以有条件地将转换为 CGRect (从而确定 T == CGRect ),Swift无法推断出这些信息,因此禁止尝试返回 CGRect 当它期望返回 T



因此,粗略的解决方案是强制投射回报结果回到 T ,以便弥补信息与类型系统的差距:

  if let from = from as? CGRect,让=去as? CGRect {

// ...

返回returnRect as! T
}

然而,这种类型演绎真的是一个标志,重新对抗类型系统,而不是利用泛型提供的静态类型,因此不推荐。



更好的解决方案,如 @Wongzigii已经说过了,就是使用一个协议。例如,如果您在他的答案中显示 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 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中使用泛型类型处理不同的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆