在不使用Any的情况下,如何在Swift中存储具有相关类型的协议数组?

时间:2019-06-23 21:22:08

标签: swift generics protocols type-erasure

我正在做这样的事情:

protocol StateType { }

struct DogState: StateType { }

struct CatState: StateType { }

protocol Action {
    associatedType ST
    func process() -> ST
}

struct DogAction {
    func process() -> DogState { return DogState() }
}

struct CatAction {
    func process() -> CatState { return CatState() }
}

let actions = [DogAction(), CatAction()]

actions.forEach {
    $0.process()
}

我曾尝试用类型擦除来摆弄,但是我无法解决无法创建异构数组的问题。

struct AnyActionProtocol<A: Action, ST: StateType>: Action  where A.StateType == ST {
    let action: A

    init(_ action: A) {
        self.action = action
    }

    func process() -> ST {
        return action.process()
    }
}

let dap = AnyActionProtocol<DogAction, DogState>(DogAction())
let cap = AnyActionProtocol<CatAction, CatState>(CatAction())

当我尝试此操作时,我得到的错误是异构集合常量只能推断为[Any],这是我要避免的事情。

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

PAT通常很复杂,并且不涉及更简单的解决方案。我建议您针对您的问题提出一些更简单的设计。您的方法的问题实际上是具有PAT和嵌套协议,并试图使它们协同工作。

即使您将擦除Action键入类似AnyAction的某种类型,这两种不同的类型DogActionCatAction也会再次产生不同的类型,并且您不能在数组内将它们混合在一起。您可能要做的是进行两种类型的擦除,一种用于Action,另一种用于StateType。当您将CatActionDogAction包装在AnyAction内时,它们都将以类型擦除状态包装到AnyState

这里是一个示例,您可以使用这种类型来擦除两种协议,

protocol StateType { }

struct DogState: StateType { }

struct CatState: StateType { }

protocol Action {
    associatedtype ST: StateType
    func process() -> ST
}

struct DogAction: Action {
    func process() -> DogState { return DogState() }
}

struct CatAction: Action {
    func process() -> CatState { return CatState() }
}

struct AnyState: StateType {
    let state: StateType

    init(_ state: StateType) {
        self.state = state
    }
}


struct AnyAction: Action {
    typealias ST = AnyState

    let processor: () -> AnyState

    init<T: Action>(_ a: T)  {
        self.processor = {
            return AnyState(a.process())
        }
    }

    func process() -> AnyState {
        return processor()
    }
}


let cat = AnyAction(CatAction())
let dog = AnyAction(DogAction())

let actions = [cat, dog]

actions.forEach { action in
    action.process()
}

我仍然认为您应该重新考虑解决方案,随着类型的增加,这可能会变得更加复杂。