Swift 中的泛型数组 [英] Arrays of Generics in Swift

查看:84
本文介绍了Swift 中的泛型数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究具有不同类型的泛型类数组.用一些示例代码来解释我的问题最简单:

I've been playing around with arrays of generic classes with different types. It's easiest to explain my problem with some sample code:

// Obviously a very pointless protocol...
protocol MyProtocol {
    var value: Self { get }
}

extension Int   : MyProtocol {  var value: Int    { return self } }
extension Double: MyProtocol {  var value: Double { return self } }

class Container<T: MyProtocol> {
    var values: [T]

    init(_ values: T...) {
        self.values = values
    }

    func myMethod() -> [T] {
        return values
    }
}

现在,如果我尝试像这样创建一个容器数组:

Now if I try to create an array of containers like so:

var containers: [Container<MyProtocol>] = []

我收到错误:

协议MyProtocol"只能用作通用约束,因为它具有 Self 或相关的类型要求.

Protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements.

为了解决这个问题,我可以使用 [AnyObject]:

To fix this I can use [AnyObject]:

let containers: [AnyObject] = [Container<Int>(1, 2, 3), Container<Double>(1.0, 2.0, 3.0)]
// Explicitly stating the types just for clarity.

但是现在在通过容器进行枚举时出现了另一个问题":

But now another 'problem' emerges when enumerating through containers:

for container in containers {
    if let c = container as? Container<Int> {
        println(c.myMethod())

    } else if let c = container as? Container<Double> {
        println(c.myMethod())
    }
}

正如你在上面的代码中看到的,在确定了 container 的类型后,在两种情况下都会调用相同的方法.我的问题是:

As you can see in the code above, after determining the type of container the same method is called in both cases. My question is:

是否有比强制转换为每种可能的 Container 类型更好的方法来获取具有正确类型的 Container?或者还有什么我忽略的地方?

Is there a better way to get the Container with the correct type than casting to every possible type of Container? Or is there something else I've overlooked?

推荐答案

有一种方法 - 有点 - 做你想做的 - 有点.有一种方法可以使用协议来消除类型限制并仍然获得您想要的结果,有点,但它并不总是很漂亮.这是我在您的情况下提出的协议:

There is a way - sort of - to do what you want - kind of. There is a way, with protocols, to eliminate the type restriction and still get the result that you want, kind of, but it isn't always pretty. Here is what I came up with as a protocol in your situation:

protocol MyProtocol {
    func getValue() -> Self 
}

extension Int: MyProtocol {
    func getValue() -> Int {
        return self
    }
}

extension Double: MyProtocol {
    func getValue() -> Double {
        return self
    }
}

请注意,您最初放在协议声明中的 value 属性已更改为返回对象的方法.

Note that the value property that you originally put in your protocol declaration has been changed to a method that returns the object.

那不是很有趣.

但是现在,因为您已经摆脱了协议中的 value 属性,MyProtocol 可以用作类型,而不仅仅是类型约束.您的 Container 类甚至不再需要是通用的.你可以这样声明:

But now, because you've gotten rid of the value property in the protocol, MyProtocol can be used as a type, not just as a type constraint. Your Container class doesn't even need to be generic anymore. You can declare it like this:

class Container {
    var values: [MyProtocol]

    init(_ values: MyProtocol...) {
        self.values = values
    }

    func myMethod() -> [MyProtocol] {
        return values
    }
}

并且因为 Container 不再是通用的,您可以创建 ContainerArray 并遍历它们,打印结果myMethod() 方法:

And because Container is no longer generic, you can create an Array of Containers and iterate through them, printing the results of the myMethod() method:

var containers = [Container]()

containers.append(Container(1, 4, 6, 2, 6))
containers.append(Container(1.2, 3.5))

for container in containers {
    println(container.myMethod())
}

//  Output: [1, 4, 6, 2, 6]
//          [1.2, 3.5]

诀窍是构建一个协议,只包含泛型函数,并且对符合的类型没有其他要求.如果你能做到这一点,那么你可以将协议用作类型, 而不仅仅是作为类型约束.

The trick is to construct a protocol that only includes generic functions and places no other requirements on a conforming type. If you can get away with doing that, then you can use the protocol as a type, and not just as a type constraint.

作为奖励(如果您想这样称呼它),您的 MyProtocol 值数组甚至可以混合符合 MyProtocol 的不同类型.因此,如果您给 String 一个 MyProtocol 扩展,如下所示:

And as a bonus (if you want to call it that), your array of MyProtocol values can even mix different types that conform to MyProtocol. So if you give String a MyProtocol extension like this:

extension String: MyProtocol {
    func getValue() -> String {
        return self
    }
}

你实际上可以用混合类型初始化一个Container:

You can actually initialize a Container with mixed types:

let container = Container(1, 4.2, "no kidding, this works")

[警告 - 我正在其中一个在线游乐场中对此进行测试.我还没有能够在 Xcode 中测试它...]

[Warning - I am testing this in one of the online playgrounds. I haven't been able to test it in Xcode yet...]

如果你仍然希望 Container 是通用的并且只保存一种类型的对象,你可以通过让 符合它自己的协议来实现:

If you still want Container to be generic and only hold one type of object, you can accomplish that by making it conform to its own protocol:

protocol ContainerProtocol {
    func myMethod() -> [MyProtocol]
}

class Container<T: MyProtocol>: ContainerProtocol {
    var values: [T] = []

    init(_ values: T...) {
        self.values = values
    } 

    func myMethod() -> [MyProtocol] {
        return values.map { $0 as MyProtocol }
    }
}

现在你可以仍然拥有一个 [ContainerProtocol] 对象的数组,并通过调用 myMethod() 遍历它们:

Now you can still have an array of [ContainerProtocol] objects and iterate through them invoking myMethod():

let containers: [ContainerProtocol] = [Container(5, 3, 7), Container(1.2, 4,5)]

for container in containers {
    println(container.myMethod())
}

也许这仍然不适合您,但现在 Container 仅限于单一类型,但您仍然可以遍历 ContainterProtocol 对象的数组.

Maybe that still doesn't work for you, but now Container is restricted to a single type, and yet you can still iterate through an array of ContainterProtocol objects.

这篇关于Swift 中的泛型数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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