我一直在玩不同类型的泛型类数组 . 用一些示例代码解释我的问题最容易:
// 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>] = []
我收到错误:
协议'MyProtocol'只能用作通用约束,因为它具有Self或相关类型要求 .
要解决这个问题,我可以使用 [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
进行枚举时会出现另一个'problem':
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
的类型之后,在两种情况下都会调用相同的方法 . 我的问题是:
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?
5 回答
有一种方式 - 有点 - 做你想做的事 - 有点儿 . 有一种方法,有协议,消除类型限制,仍然得到你想要的结果,但它并不总是漂亮 . 以下是我在您的情况下作为协议提出的内容:
请注意,最初放在协议声明中的
value
属性已更改为返回对象的方法 .那不是很有趣 .
但是现在,因为你已经摆脱了协议中的
value
属性,MyProtocol
可以用作一个类型,而不仅仅是一个类型约束 . 你的Container
类甚至不需要是通用的了 . 您可以这样声明:因为
Container
不再是通用的,你可以创建一个Array
的Container
并迭代它们,打印myMethod()
方法的结果:诀窍是构建一个仅包含通用函数的协议,并且对符合类型没有其他要求 . 如果您可以逃脱这样做,那么您可以将协议用作类型,而不仅仅是类型约束 .
作为奖励(如果你想称之为),你的
MyProtocol
值数组甚至可以混合符合MyProtocol
的不同类型 . 所以如果你给String
这样的MyProtocol
扩展:您实际上可以使用混合类型初始化
Container
:[警告 - 我正在其中一个在线游乐场进行测试 . 我还没能在Xcode中测试它...]
编辑:
如果您仍然希望
Container
是通用的并且只保留一种类型的对象,则可以通过使其符合自己的协议来实现:现在你仍然可以拥有一个
[ContainerProtocol]
对象数组,并通过它们迭代调用myMethod()
:也许这仍然不适合你,但现在
Container
被限制为单一类型,但你仍然可以迭代ContainterProtocol
对象的数组 .这是“你想要发生什么?”的一个很好的例子 . 并且实际上展示了如果Swift真的具有一流类型而爆炸的复杂性 .
大 .
MyProtocol.value
返回任何类型实现它,记住这必须在编译时确定,而不是运行时 .那么,在编译时确定,这是什么类型的?忘记编译器,只需在纸上完成 . 是的,不知道会是什么类型 . 我的意思是具体类型 . 没有元型 .
AnyObject
偷偷溜进你的签名时,你知道你走错了路 . 关于这一点的任何事情都无法发挥作用 . 之后AnyObject
只是麻布 .是 . 你需要一个类型,但你没有提供 . 您提供了约束类型的规则,但没有实际类型 . 回到你真正的问题,并更深入地思考它 . (元数据分析几乎不是你的“真正的”问题,除非你正在攻读CS博士,在这种情况下你会在伊德里斯做这个,而不是斯威夫特 . )你解决了什么实际问题?
使用
Equatable
等协议可以更好地解释这一点 . 您不能声明数组[Equatable]
,因为虽然可以将两个Int
实例相互比较,并且可以将两个Double
实例相互比较,但您无法将Int
与Double
进行比较,尽管它们都实现Equatable
.MyProtocol
是一种协议,这意味着它提供了一个通用接口 . 不幸的是,您还在定义中使用了Self
. 这意味着符合MyProtocol
的每种类型都将以不同方式实现它 .你自己写了 -
Int
将value
作为var value: Int
而MyObject
将value
作为var value: MyObject
.这意味着不能使用符合
MyProtocol
的结构/类符合MyProtocol
的另一个结构/类的位置 . 这也意味着您不能以这种方式使用MyProtocol
,而无需指定具体类型 .如果用具体类型替换
Self
,例如AnyObject
,它会起作用 . 但是,目前(Xcode 6.3.1)它在编译时会触发分段错误) .如果您在游乐场中尝试此修改示例,它将系统崩溃:
可能他们仍在努力解决这个问题,未来可能会发生变化 . 无论如何,到目前为止,我对此的解释是协议不是具体类型 . 因此,现在你没有在ram中有多少符合协议的东西(例如
Int
可能不会占用与Double
相同数量的ram) . 因此,在ram中分配数组可能是一个非常棘手的问题 . 使用NSArray
你分配一个指针数组(指向NSObjects
的指针),它们都占用相同数量的ram . 您可以将NSArray
视为具体类型“指向NSObject
的指针”的数组 . 因此计算ram分配没有问题 .考虑到Swift中的
Array
以及Dictionary
是Generic Struct,而不是像Obj-C中那样包含指向对象的指针的对象 .希望这可以帮助 .
我将数组声明更改为AnyObject数组,以便可以使用filter,map和reduce(并且还可以添加一些对象来检查) .
这将允许您在循环遍历数组之前检查数组中的类型和过滤器