【问题标题】:Generics in Swift 2.0Swift 2.0 中的泛型
【发布时间】:2015-06-23 08:15:41
【问题描述】:

我浏览过 Apple 开发者网站上的 Swift 教程,但我不了解泛型的概念。有没有人能用简单的方式解释它?例如:

func swapTwoValues<T>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

【问题讨论】:

  • 好问题,如果有人满足您的问题,您应该考虑选择此问题的答案!这可以提高您的帖子质量,从而使社区受益。

标签: ios swift generics swift2


【解决方案1】:

如果在您给出的示例中不使用泛型,您将不得不为您想要交换的每种类型重载 swapTwoValues。例如:

func swapTwoValues(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}

func swapTwoValues(inout a: String, inout b: String) {
    let temp = a
    a = b
    b = temp
} 

// Many more swapTwoValues functions...

上述函数之间唯一不同的是它们接受的类型;每个内部的代码完全相同。因此,最好编写 一个 可以采用任何类型的泛型函数。

请务必注意,您不能将 T 替换为 Any。不能保证 ab 是同一类型 - 例如,您不能交换 IntString

【讨论】:

    【解决方案2】:

    在您的示例中,T 代表一个类型。并且一旦设置该类型对于整个函数都是一致的。

    func swapTwoValues<T>(inout a: T, inout b: T) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    

    如果在参数 a 的情况下 T 是一个 Int,那么在参数 b 的情况下它也必须是一个 Int。如该函数的使用所示:

    var valueA = 2
    var valueB = 4
    swapTwoValues(&valueA, b: &valueB)
    
    valueA // 4
    valueB // 2
    

    例如,我们不能将 String 替换为 Int,甚至不能将 Int 替换为 Double,但只要 Type 相同,则此泛型方法将采用任何 Type,因为它在所有其他方面都不受限制。

    var valueA = "Hello"
    var valueB = "Swift"
    swapTwoValues(&valueA, b: &valueB)
    
    valueA // "Swift"
    valueB // "Hello"
    

    但是,这并不意味着从泛型函数中排除了多种类型。您只需分配一个不同的字母来表示不同的类型(使用的字母无关紧要,只是使用 T 是因为它是 Type 的第一个字母,但没有理由不能用 Q 代替它,例如,或任何其他字母):

    func swapTwoValues<T,S>(inout a: T, inout b: T, inout c: S, inout d: S) {
        let temporaryA = a
        a = b
        b = temporaryA
    
        let temporaryC = c
        c = d
        d = temporaryC
    }
    
    var valueA = 2
    var valueB = 4
    
    var valueC = "Hello"
    var valueD = "Swift"
    swapTwoValues(&valueA, b: &valueB, c:&valueC, d:&valueD)
    
    valueA  // 4
    valueB // 2
    
    valueC // "Swift"
    valueD // "Hello"
    

    注意:我们仍然不能将 T 替换为 S,因为 Swift 是一种强类型语言,我们无法保证它们是相同的。

    当涉及到协议来约束泛型类型时,它变得更加有趣。在这里,我使用 UnsignedIntegerType:

    func swapTwoValues<T: UnsignedIntegerType>(inout a: T, inout b: T) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    var valueA:UInt = 10
    var valueB:UInt = 11
    
    swapTwoValues(&valueA, b: &valueB)
    

    现在只接受 UInt、UInt8、UInt32 等类型,所有其他值都将被拒绝并产生错误。

    注意:使用协议约束类型的原因是为了保证某些方法可以正常工作。例如,如果需要一个泛型函数来创建一个新的类型实例,那么它必须采用一个带有 init 方法的协议。 (您可以在 Apple 的文档中检查每种类型的协议采用情况。)

    我们可以更进一步,使用where 关键字来确定泛型集合中包含的类型:

    func swapTwoValues<T: CollectionType where T.Generator.Element: UnsignedIntegerType>(inout a: T, inout b: T) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    var valueA:[UInt] = [10,12,4]
    var valueB:[UInt] = [11,45,67]
    
    swapTwoValues(&valueA, b: &valueB)
    
    valueA  // [11, 45, 67]
    valueB // [10, 12, 4]
    

    或者使用==检查第二种类型是否等同于集合中元素的类型:

    func swapTwoValues<T: CollectionType, S where S == T.Generator.Element>(inout a: T, inout b: T, inout c: S, inout d: S) {
        let temporaryA = a
        a = b
        b = temporaryA
    
        let temporaryC = c
        c =  d
        d = temporaryC
    }
    

    进一步阅读:protocol extensions in Swift 2 让事情变得更加有趣,因为现在泛型函数可以具有 Type 方法的特征,这使得它们更容易被发现。

    【讨论】:

      【解决方案3】:

      本质上,它只是意味着它不是特定于类型的。您使用 T 并只编写一个函数,而不是为每种类型编写多个函数 IntDoubleFloatString 等。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多