Swift - 继承协议关联类型擦除

Swift - Inherited Protocol Associated Type Erasure

我有 3 个协议和一个必须确定最专用协议的函数

protocol Super {}
protocol Sub1: Super { associatedtype T }
protocol Sub2: Super {}

func test(_ s: Super) { ... do stuff }

现在我已经用擦除类型试过了

class AnySub<E>: Sub1 {
    typealias T = E
    // ... standard type erasure init
}

有以下变化:

protocol Super {
    func tryAsSub1() -> AnySub? {}
}
extension Sub1 { 
    func tryAsSub1() -> AnySub<T> { return AnySub<T>() }
}
extension Sub2 {
    func tryAsSub1() -> AnySub { return nil }
}
func test(_ s: Super) {
    if let sub1 = s.tryAsSub1() { ... do stuff for Sub1 }
    else let sub2 = s as? Sub2 { ... do stuff for Sub2 }
}

但这显然行不通,因为我在 Super 和 Sub2 中没有任何通用参数。 有人知道我该如何解决这个问题吗?

由于Sub1使用关联类型,您无法在运行时确定某个变量是否属于该类型。类型橡皮擦在一定程度上有所帮助,但是很难使用多种类型的橡皮擦。我的建议是为您需要处理的每种类型重载 test 方法。这也为您的代码增加了更多类型安全性。

func test<S: Sub1, T>(_ s: S) where S.T == T

func test(_ s: Sub2)

以上解决方案不适用于您拥有 Super 元素集合的场景,并且您需要根据实际类型执行一些操作。对于这种情况,一种可能的方法是在协议级别移动 test 方法,并在子协议中覆盖。

protocol Super {
    func test()
}

protocol Sub1: Super { associatedtype T }

protocol Sub2: Super {}

extension Sub1 {
    func test() { ... do stuff for Sub1 }
}

extension Sub2 {
    func test() { ... do stuff for Sub2 }
}

缺点是一致性可以覆盖 test,因此你会失去原来的实现。