// 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对象数组。