简体   繁体   English

Swift 2.0中的泛型

[英]Generics in Swift 2.0

I have been through the Swift tutorials on the Apple developer site, but I do not understand the concepts of Generics. 我已经浏览了Apple开发者网站上的Swift教程,但我不理解泛型的概念。 Is anyone able to explain it in a simple way? 有人能够以简单的方式解释它吗? For example: 例如:

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

Without using generics in the example you gave, you'd have to overload swapTwoValues for every type you wanted to swap. 如果不在您给出的示例中使用泛型,则必须为要交换的每种类型重载swapTwoValues For example: 例如:

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...

The only thing that's different between the functions above is the type they accept; 上述函数之间唯一不同的是它们接受的类型; the code inside each is exactly the same. 每个内部的代码完全相同。 Therefore, it's better to write one generic function which can take any type. 因此,最好编写一个可以采用任何类型的通用函数。

It's important to note you can't substitute T with Any . 重要的是要注意你不能用Any替代T There would be no guarantee a and b would be the same type - you couldn't swap an Int and a String , for example. 不能保证ab的类型相同 - 例如,你不能交换IntString

In your example T represents a Type. 在您的示例中,T表示类型。 And once set that type is consistent for the whole of the function. 并且一旦设置该类型对于整个功能是一致的。

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

If T is an Int in the case of parameter a, then it will also have to be an Int in the case of parameter b. 如果在参数a的情况下T是Int,那么在参数b的情况下它也必须是Int。 As demonstrated in this use of the function: 正如在这个功能的使用中所证明的:

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

valueA // 4
valueB // 2

We could not swap a String for an Int for example or even an Int for a Double, but as long as Types are the same then this generic method will take any Type, because it is unrestrained in all other respects. 我们不能为一个Int交换一个String,或者为一个Double交换一个Int,但只要Types相同,那么这个泛型方法将采用任何Type,因为它在所有其他方面都是无限制的。

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

valueA // "Swift"
valueB // "Hello"

This does not mean multiple types are excluded from generic functions, however. 但是,这并不意味着从通用函数中排除了多种类型。 You simply need to assign a different letter to represent the different types (the letter used is irrelevant, T is simply used because it is the first letter of Type but there is no reason it could not be replaced with Q, for example, or any other letter): 你只需要指定一个不同的字母来表示不同的类型(使用的字母是无关紧要的,简单地使用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"

Note: we still cannot swap a T for an S, because Swift is a strongly typed language and we have no reassurance they are the same. 注意:我们仍然无法将T替换为S,因为Swift是一种强类型语言,我们无法保证它们是相同的。

It becomes more interesting when protocols are involved to constrain the generic types. 当协议涉及约束泛型类型时,它变得更有趣。 Here I do so with the UnsignedIntegerType: 这里我使用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)

Now only types such as UInt, UInt8, UInt32, etc are acceptable and all other values will be rejected and create an error. 现在只有UInt,UInt8,UInt32等类型可以接受,所有其他值都将被拒绝并产生错误。

Note: The reason for constraining types using protocols is so that certain methods can be guaranteed to work. 注意:使用协议约束类型的原因是可以保证某些方法可以工作。 For example if a generic function is required to create a new type instance, then it must adopt a protocol with an init method. 例如,如果创建新类型实例需要通用函数,则必须采用带有init方法的协议。 (You can check protocol adoption for each type in Apple's docs.) (您可以在Apple的文档中检查每种类型的协议采用情况。)

We can go further and use the where keyword to determine the types contained within a generic collection: 我们可以进一步使用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]

Or do things like check that a second type is equivalent to the type of Elements in a collection using == : 或者使用==检查第二种类型是否与集合中的Elements类型等效:

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
}

Further reading: Things get even more interesting with protocol extensions in Swift 2 because now generic functions can take on the characteristics of Type methods, which makes them far more discoverable. 进一步阅读: Swift 2中的协议扩展使事情变得更加有趣,因为现在泛型函数可以采用Type方法的特性,这使得它们更容易被发现。

Essentially, it just means that it is not type specific. 从本质上讲,它只是意味着它不是特定于类型的。 You use T and just write one function instead of writing many functions for each type Int , Double , Float , String etc. 您使用T并只编写一个函数,而不是为每个类型IntDoubleFloatString等编写许多函数。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM