简体   繁体   English

如何快速使用通用类型处理不同类型?

[英]How can I handle different types using generic type in swift?

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'. 不幸的是,它在return returnRect时给了我一个错误:无法将'CGRect'类型的返回表达式转换为'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. 也许我不了解泛型的用法...我只是想拥有一个可以处理各种类型之间的插值的函数,而不是拥有像func interpolate(from: Int, to: Int)func interpolate(from: CGPoint, to: CGPoint) ,等等。

It would be nice if you use Protocol to scale your generic type. 如果您使用Protocol来扩展通用类型,那将是很好的。

protocol Interpolate {
    associatedtype T
    static func interpolate(from: T, to: T, progress: CGFloat) -> T
}

Then let CGRect extension conform to your protocol: 然后让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)

Also, this would make NSString conform to protocol Interpolate easily, like: 此外,这将使NSString轻松符合协议Interpolate ,例如:

extension NSString: Interpolate {
    typealias T = NSString
    static func interpolate(from: T, to: T, progress: CGFloat) -> NSString.T {
        //...
        return "" 
    }
}

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. 问题是T是一个通用占位符-这意味着您无法从函数中知道T的实际具体类型是什么。 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 . 因此,尽管你可以有条件地投fromtoCGRect (从而建立该T == CGRect ),斯威夫特是无法推断出这一信息,因此禁止试图返回一个CGRect时,预计的回报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: 因此,粗略的解决方案是将返回结果强制转换回T ,以弥合这种信息与类型系统的差距:

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. 正如@Wongzigii已经说过的 ,更好的解决方案是使用协议。 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: 例如,如果您定义了他在其答案中显示的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)
    }
}

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. 这解决了您的许多问题–消除了运行时类型转换,而是使用协议约束来调用专用interpolate函数。 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. 协议约束还防止您在编译时传递不符合Interpolate任何类型,因此还解决了类型转换失败时的处理方法。

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. 尽管这么说,但我实际上非常喜欢@JoshCaswell在他对其他问题的回答中建议的解决方案-重载运算符以实现此功能。 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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用泛型类型时,如何使用默认参数扩展 Swift 协议? - How can I extend a Swift protocol with a default parameter when using generic types? 如何对 Swift 中的所有数字类型进行通用扩展? - How can I make a generic extension on all number types in Swift? 如何在 Swift 中为泛型类型添加类型方法? - How to add type methods to generic types in Swift? 在 Swift 中,如何通过为不同的键路径注册处理器来实现处理一个类型的通用系统? - In Swift, how can I implement a generic system that processes a type by registering processors for different key paths? 如何使用 Codable 处理不同类型的 JSON 数据? - How can I handle different types of JSON data with Codable? swift:具有不同泛型类型的 AnyCollection - swift: AnyCollection with different generic types 如何键入检查通用然后在swift中转换为它? - How can I type check a generic then cast to it in swift? 如何将 nil 传递给 Swift 上的泛型类型参数? - How can I pass nil to generic type parameter on Swift? 如何使用 Swift 上的 readLine() 比较两种不同类型的数据(String 和 Int)? - How can I compare two different types of data (String and Int) using readLine() on Swift? 如何快速声明具有不同类型的二维数组? - How can I declare a two dimension array with different types in swift?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM