在斯威夫特泛型数组 [英] Arrays of Generics in Swift

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

问题描述

我一直在玩弄泛型类不同类型的数组。这是最简单的一些示例code解释我的问题:

  //显然是一个非常没有意义的协议...
协议MyProtocol {
    VAR值:自{}获取
}扩展INT:MyProtocol {VAR值:诠释{返回自}}
扩展双:MyProtocol {VAR值:双{返回自}}一流的集装箱< T:MyProtocol> {
    VAR值:[T]    的init(_值:T ...){
        self.values​​ =值
    }    FUNC myMethod的() - GT; [T] {
        返回值
    }
}

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

  VAR集装箱:集装箱< MyProtocol>] = []

我得到的错误:


  

协议'MyProtocol'只能用作通用约束,因为它有自或相关联的类型的要求。


要解决这个问题,我可以使用 [AnyObject]

 让容器:[AnyObject] = [容器<智力及GT;(1,2,3),容器和LT;双>(1.0,2.0,3.0)]
//显式声明的类型仅仅是为了清楚。

但是,现在通过容器枚举当另一个问题出现

 在集装箱货柜{
    如果让C =容器作为?集装箱<&诠释GT; {
        的println(c.myMethod())    }否则,如果让C =容器作为?集装箱<双> {
        的println(c.myMethod())
    }
}

正如你可以在上面的code见,确定容器的类型后同样的方法被调用在两种情况下。我的问题是:

有没有更好的办法让集装箱正确类型比铸造到每一个可能的类型集装箱?还是有别的东西,我忽略了?


解决方案

有一个办法 - 排序 - 做你想做什么 - 样的。有一种方法,以协议,以消除类型限制,仍然可以得到你想要那种,结果,但它并不总是pretty。以下是我想出了为您的情况的协议:

 协议MyProtocol {
    FUNC的getValue() - GT;自
}扩展INT:MyProtocol {
    FUNC的getValue() - GT; INT {
        自回归
    }
}扩展双:MyProtocol {
    FUNC的getValue() - GT;双{
        自回归
    }
}

请注意,属性您最初把你的协议的声明已更改为返回对象的方法。

这是不是很有趣。

但现在,因为你已经得到摆脱价值在协议属性, MyProtocol 可使用作为一种类型,而不仅仅是一个类型约束。你的集装箱类甚至不需要是通用了。你可以声明它是这样的:

 类容器{
    VAR值:[MyProtocol]    的init(_值:MyProtocol ...){
        self.values​​ =值
    }    FUNC myMethod的() - GT; [MyProtocol] {
        返回值
    }
}

而因为集装箱不再是通用的,你可以创建阵列集装箱和遍历它们,打印 myMethod的()方法的结果:

  VAR容器= [容器]()containers.append(容器(1,4,6,2,6))
containers.append(集装箱(1.2 3.5))在集装箱货柜{
    的println(container.myMethod())
}//输出:[1,4,6,2,6]
// [1.2,3.5]

关键是要构建的的协议只包括通用功能,而且对一个符合要求的类型没有其他要求。的如果你能逃脱这样做,那么你可以使用的协议作为类型,而不只是作为一个类型约束

作为奖励(如果你想这样称呼它),你的 MyProtocol 值数组甚至可以混合使用符合不同类型的 MyProtocol 。所以,如果你给字符串 A MyProtocol 延伸是这样的:

 扩展字符串:MyProtocol {
    FUNC的getValue() - GT;字符串{
        自回归
    }
}

实际上,你可以初始化一个集装箱混合类型:

 让容器=容器(1,4.2,别开玩笑了,这个工程)

[警告 - 我在网上游乐场之一测试此。我一直无法对其进行测试以x code又...]

编辑:

如果您仍然希望集装箱是通用的,只能持有一种类型的对象,你可以通过实现这个目标的的符合自己的协议:

 协议ContainerProtocol {
    FUNC myMethod的() - GT; [MyProtocol]
}一流的集装箱< T:MyProtocol计算值:ContainerProtocol {
    VAR值:[T] = []    的init(_值:T ...){
        self.values​​ =值
    }    FUNC myMethod的() - GT; [MyProtocol] {
        返回values​​.map {$ 0作为MyProtocol}
    }
}

现在,你可以的还是的有 [ContainerProtocol] 对象的数组,并遍历它们调用 myMethod的()

 让容器:[ContainerProtocol] = [容器(5,3,7),集装箱(1.2,4,5)]在集装箱货柜{
    的println(container.myMethod())
}

也许这仍然不能为你工作,但现在集装箱仅限于单一类型的,但你可以通过<$ C $阵列仍然迭代C> ContainterProtocol 的对象。

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>] = []

I get the error:

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

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())
    }
}

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:

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

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

That's not very interesting.

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

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.

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

You can actually initialize a Container with mixed types:

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

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

Edit:

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

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())
}

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.

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

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