如何定义符合协议的对象数组?

8

给定:

protocol MyProtocol {
    typealias T
    var abc: T { get }
}

实现MyProtocol的类:

class XYZ: MyProtocol {
    typealias T = SomeObject
    var abc: T { /* Implementation */ }
}

我该如何定义一个符合 MyProtocol 的对象数组?
var list = [MyProtocol]()

(连同大量的SourceKit崩溃)会产生以下错误:
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements

虽然 typealias 实际上是在 MyProtocol 中定义的,但是我们可以将它放在全局作用域中使用。

有没有一种方法可以列出符合协议并具有泛型约束的对象列表?


@Paulw11 对不起,我完全忽略了那个。 - erudel
1个回答

3
问题是关于使用泛型协议、类型别名的对应项。听起来有些奇怪,但如果你定义了一个类型别名,你就不能将协议用作类型,这意味着你不能声明该协议类型的变量、函数参数等,也不能将其用作数组的泛型对象。
正如错误提示所说,你唯一能够使用它的方式是作为泛型约束(比如在 class Test<T:ProtocolWithAlias> 中)。
为了证明这一点,只需从你的协议中删除类型别名(注意,这只是为了证明,不是解决方案)。
protocol MyProtocol {
    var abc: Int { get }
}

并相应修改示例代码的其余部分:
class XYZ: MyProtocol {
    var abc: Int { return  32 }
}

var list = [MyProtocol]()

你会注意到它可行。
你可能更感兴趣的是如何解决这个问题。我还没想到任何优雅的解决方案,只有以下两种:
- 从协议中删除类型别名,并将 T 替换为 AnyObject(丑陋的解决方案!) - 将该协议变成类(但这并不是所有情况下都适用的解决方案)
但正如你可能会争辩的那样,我都不喜欢上述两种方法。我唯一能提供的建议就是重新思考设计,看看是否可以使用不同的方式(即不使用类型别名的协议)来实现相同的结果。

如果你仔细想一下,这其实是有道理的。由于 abc 的返回类型在编译时是未知的,所以你不能可靠地以类型安全的方式使用结果。一个 MyProtocol 数组是没有意义的,因为它们并不都具有相同的功能。 - David Berry
1
@David:我不同意这个观点。带有泛型的结构体和类都有同样的问题。其他语言(例如C#,Java等)允许使用带有泛型的协议。而且,MyProtocol数组确实具有相同的功能-不同的是每个元素可以以不同的方式实现功能-但是将基类用作数组元素类型并放置从中继承的对象实例时,相同的概念也适用。 - Antonio
@Antonio 谢谢,我认为强制类型转换可能是这里唯一的解决方案。在之前的尝试中,我确实使用了一个类泛型参数,但是 Swift 不允许(目前)有一个继承自泛型类型的非泛型类型 - XYZ:ABC <Int> 是不允许的。 - erudel
@erudel 是的,这是我错过的 Swift 的另一件事情...虽然不像拥有可用的通用协议那样重要。 - Antonio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接