Swift中的generics数组

我一直在玩与不同types的generics类的数组。 用一些示例代码来解释我的问题是最简单的:

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

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

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

我得到的错误:

协议“MyProtocol”只能用作通用约束,因为它具有自我或相关types的要求。

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

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

但是现在当通过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的types之后,在两种情况下调用相同的方法。 我的问题是:

有没有更好的方式来获得正确types的Container ,而不是投射到每种可能types的Container 还是有什么我忽略了?

有一种方法可以做你想做的事情。 有一种方式,通过协议,消除types限制,仍然得到你想要的结果,种类,但它并不总是漂亮。 下面是我在你的情况下提出的协议:

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

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

这不是很有趣。

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

 class Container { var values: [MyProtocol] init(_ values: MyProtocol...) { self.values = values } func myMethod() -> [MyProtocol] { return values } } 

由于Container不再是通用的,因此可以创build一个Container Array并遍历它们,并打印myMethod()方法的结果:

 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] 

诀窍是构build一个只包含generics函数的协议, 并且对合规types没有其他要求。 如果你能这样做,那么你可以使用协议作为types,而不仅仅是一个types约束。

作为奖励(如果你想这样做的话),你的MyProtocol值数组甚至可以混合符合MyProtocol不同types。 所以如果你给String一个MyProtocol扩展,像这样:

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

实际上你可以使用混合types初始化一个Container

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

[警告 – 我正在其中一个在线游乐场进行testing。 我还没有能够在Xcode中testing它…]

编辑:

如果你仍然希望Container是通用的,只保存一种types的对象,你可以通过使其符合自己的协议来实现:

 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()遍历它们:

 let containers: [ContainerProtocol] = [Container(5, 3, 7), Container(1.2, 4,5)] for container in containers { println(container.myMethod()) } 

也许这仍然不适合你,但现在Container被限制为一种types,但仍然可以遍历一个ContainterProtocol对象的数组。

这是“你想要发生什么?”的一个很好的例子。 而实际上,如果Swift真的具有一stream的types,实际上就展现了爆炸的复杂性。

 protocol MyProtocol { var value: Self { get } } 

大。 MyProtocol.value返回任何types的实现它,记住这必须在编译时确定,而不是运行时。

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

那么,在编译时确定,这是什么types? 忘记编译器,只是在纸上做。 是的,不知道是什么types的。 我的意思是具体types。 没有元types。

 let containers: [AnyObject] = [Container<Int>(1, 2, 3), Container<Double>(1.0, 2.0, 3.0)] 

AnyObject偷偷签名时,你知道你走错了路。 没有什么关于这是永远不会工作。 AnyObject之后只有麻布。

还是有什么我忽略了?

是。 你需要一个types,你没有提供一个。 你已经提供了一个限制types的规则,但是没有实际的types。 回到你真正的问题,并深入思考。 (Metatype分析几乎不是你的“真正的”问题,除非你在CS博士工作,在这种情况下,你会在Idris,而不是Swift做这个)。你究竟在解决什么问题?

这可以用像Equatable这样的协议来解释。 你不能声明一个数组[Equatable]因为虽然两个Int实例可以相互比较,并且Double两个实例可以相互比较,但不能将IntDouble进行比较,尽pipe它们都实现了Equatable

MyProtocol是一个协议,这意味着它提供了一个通用的接口。 不幸的是,你也在定义中使用了Self 。 这意味着每个符合MyProtocoltypes都会以MyProtocol方式实现。

你已经自己写了 – Int将有value作为var value: IntMyObjectvalue作为var value: MyObject

这意味着符合MyProtocol的结构/类不能用于符合MyProtocol的另一个结构/类。 这也意味着你不能以这种方式使用MyProtocol ,而不指定具体的types。

如果你用一个具体的types来replace这个Self ,比如AnyObject ,它就可以工作。 但是,目前(Xcode 6.3.1)编译时会触发分段错误)。

如果您在操场上尝试这个修改过的示例,系统将会崩溃:

 // Obviously a very pointless protocol... protocol MyProtocol { var value: Int { 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 } } var containers: [Container<MyProtocol>] = [] 

可能他们仍然在为此工作,事情可能会在未来发生变化。 无论如何,我现在的解释是协议不是一个具体的types 。 因此,现在不需要多less空间符合协议需要的内存(例如Int可能不会占用与Double相同的内存)。 因此,在RAM中分配数组可能是一个相当棘手的问题。 使用一个NSArray你分配一个指针数组(指向NSObjects指针),它们都占用相同数量的内存。 你可以把NSArray想象成一个具体types “指向NSObject ”的数组。 因此计算内存分配没有问题。

考虑到Array和Swift中的Dictionary都是Generic Struct ,而不是Obj-C中包含指向对象的指针的对象

希望这可以帮助。

我将数组声明更改为AnyObject数组,以便可以使用filter,映射和缩减(还可以添加更多的对象来检查)。

 let containers: [AnyObject] = [Container<Int>(1, 2, 3), Container<Double>(1.0, 2.0, 3.0), "Hello", "World", 42] 

这将允许您检查数组中的types并在循环数组之前进行筛选

 let strings = containers.filter({ return ($0 is String) }) println(strings) // [Hello, World] for ints in containers.filter({ return ($0 is Int) }) { println("Int is \(foo)") // Int is 42 } let ints = containers.filter({ return ($0 is Container<Int>) }) // as this array is known to only contain Container<Int> instances, downcast and unwrap directly for i in ints as [Container<Int>] { // do stuff println(i.values) // [1, 2, 3] }