- // 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
- }
- }
现在,如果我尝试创建一个容器数组,像这样:
- var containers: [Container<MyProtocol>] = []
我得到的错误:
Protocol ‘MyProtocol’ can only be used as a generic constraint because it has Self or associated type requirements.
要解决这个问题,我可以使用[AnyObject]:
- let containers: [AnyObject] = [Container<Int>(1,2,3),Container<Double>(1.0,2.0,3.0)]
- // Explicitly stating the types just for clarity.
但是现在在通过容器枚举时出现了另一个“问题”:
- 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())
- }
- }
正如你可以在上面的代码中看到的,在确定容器的类型后,在这两种情况下调用相同的方法。我的问题是:
有没有更好的方法来获得正确的类型的容器,而不是铸造到每一种可能的类型的容器?还是有其他我忽略了?
- protocol MyProtocol {
- func getValue() -> Self
- }
- extension Int: MyProtocol {
- func getValue() -> Int {
- return self
- }
- }
- extension Double: MyProtocol {
- func getValue() -> Double {
- return self
- }
- }
请注意,您最初放入协议声明中的value属性已更改为返回对象的方法。
这不是很有趣。
但是现在,因为你已经摆脱了协议中的value属性,MyProtocol可以作为一个类型,而不只是作为一个类型约束。你的Container类甚至不需要是通用的了。你可以这样声明:
- class Container {
- var values: [MyProtocol]
- init(_ values: MyProtocol...) {
- self.values = values
- }
- func myMethod() -> [MyProtocol] {
- return values
- }
- }
并且因为Container不再是通用的,你可以创建一个容器数组并迭代它们,打印myMethod()方法的结果:
- var containers = [Container]()
- containers.append(Container(1,4,6,6))
- containers.append(Container(1.2,3.5))
- for container in containers {
- println(container.myMethod())
- }
- // Output: [1,6]
- // [1.2,3.5]
诀窍是构造一个只包括通用函数的协议,并且没有对符合类型的其他要求。如果你可以逃避这样做,那么你可以使用协议作为一个类型,而不只是作为一个类型约束。
并且作为奖励(如果你想调用它),MyProtocol值的数组甚至可以混合符合MyProtocol的不同类型。所以如果你给String一个MyProtocol扩展,像这样:
- extension String: MyProtocol {
- func getValue() -> String {
- return self
- }
- }
您可以实际初始化混合类型的容器:
- let container = Container(1,4.2,"no kidding,this works")
[警告 – 我在一个在线游乐场测试这个。我还没有能够在Xcode中测试…]
编辑:
如果你仍然希望Container是通用的并且只持有一种类型的对象,你可以通过使它符合自己的协议来实现:
- 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,5)]
- for container in containers {
- println(container.myMethod())
- }
也许仍然不能为你工作,但现在Container限制为一个单一的类型,但你仍然可以迭代通过一个ContainterProtocol对象数组。