在斯威夫特泛型数组 [英] Arrays of Generics in Swift
问题描述
我一直在玩弄泛型类不同类型的数组。这是最简单的一些示例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
。所以,如果你给字符串
AMyProtocol
延伸是这样的:扩展字符串: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 ofContainer
? 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. YourContainer
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 anArray
ofContainer
s and iterate through them, printing the results of themyMethod()
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 toMyProtocol
. So if you giveString
aMyProtocol
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 invokingmyMethod()
: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 ofContainterProtocol
objects.这篇关于在斯威夫特泛型数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!