协议类型数组 [英] Array of protocol type

查看:82
本文介绍了协议类型数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在stackoverflow上检查了有关此问题的所有答案,但仍然不知道如何解决此问题. 我的模型看起来像这样

I have checked all answers about this problem on stackoverflow, but still can not figure out how to fix this. My model looks like this

protocol Commandable: Equatable {
    var condition: Condition? {get set}

    func execute() -> SKAction
}

以及实现此协议的3个结构

And 3 structs which implement this protocol

struct MoveCommand: Commandable {

    var movingVector: CGVector!

    //MARK: - Commandable

    var condition: Condition?
    func execute() -> SKAction {
       ...
    }
}

extension MoveCommand {
    // MARK:- Equatable

    static func ==(lhs: MoveCommand, rhs: MoveCommand) -> Bool {
        return lhs.movingVector == rhs.movingVector && lhs.condition == rhs.condition
    }
}


struct RotateCommand: Commandable {
    var side: RotationSide!

    // MARK: - Commandable

    var condition: Condition?
    func execute() -> SKAction {
        ...
    }
}

extension RotateCommand {
    // MARK: - Equatable
    static func ==(lhs: RotateCommand, rhs: RotateCommand) -> Bool {
        return lhs.side == rhs.side && lhs.condition == rhs.condition
    }
}


当我尝试创建具有[Commandable]数组的第三个结构时,问题就开始了:


The problems start when I am trying to create third structure which has array of [Commandable]:

struct FunctionCommand: Commandable {
    var commands = [Commandable]()

编译器输出:Protocol 'Commandable' can only be used as a generic constraint because it has Self or associated type requirements.然后我以这种方式重写了我的结构:

The compiler output: Protocol 'Commandable' can only be used as a generic constraint because it has Self or associated type requirements. Then i rewrote my struct in this way:

struct FunctionCommand<T : Equatable>: Commandable {
    var commands = [T]()

我已解决此问题,但出现了新问题.现在,我无法使用Rotate and Move命令的实例创建FunctionCommand,而只能使用其中之一的实例:(:

I resolve this problem but new problem has appeared. Now i can't create FunctionCommand with instances of Rotate and Move command, only with instances of one of them :( :

let f = FunctionCommand(commands: [MoveCommand(movingVector: .zero, condition: nil), 
RotateCommand(side: .left, condition: nil)], condition: nil)

任何帮助将不胜感激.

更新:该文章帮助我弄清了-

Update: That article helped me to figure out - https://krakendev.io/blog/generic-protocols-and-their-shortcomings

推荐答案

您需要做的是使用类型擦除,就像AnyHashable在Swift标准库中一样.

What you need to do is to use type erasure, much like AnyHashable does in the Swift Standard Library.

你不能做:

var a: [Hashable] = [5, "Yo"]
// error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements

您需要做的是使用类型擦除的类型AnyHashable:

What you have to do is to use the type-erased type AnyHashable:

var a: [AnyHashable] = [AnyHashable(5), AnyHashable("Yo")]
a[0].hashValue // => shows 5 in a playground

因此,您的解决方案是先将协议分成较小的部分,然后将Equatable升级为Hashable(以重用AnyHashable)

So your solution would be to first split the protocol in smaller parts and promote Equatable to Hashable (to reuse AnyHashable)

protocol Conditionable {
    var condition: Condition? { get set }
}

protocol Executable {
    func execute() -> SKAction
}

protocol Commandable: Hashable, Executable, Conditionable {}

然后创建一个AnyCommandable结构,如下所示:

Then create an AnyCommandable struct, like this:

struct AnyCommandable: Commandable, Equatable {
    var exeBase: Executable
    var condBase: Conditionable
    var eqBase: AnyHashable

    init<T: Commandable>(_ commandable: T) where T : Equatable {
        self.condBase = commandable
        self.exeBase = commandable
        self.eqBase = AnyHashable(commandable)
    }

    var condition: Condition? {
        get {
            return condBase.condition
        }
        set {
            condBase.condition = condition
        }
    }

    var hashValue: Int {
        return eqBase.hashValue
    }

    func execute() -> SKAction {
        return exeBase.execute()
    }

    public static func ==(lhs: AnyCommandable, rhs: AnyCommandable) -> Bool {
        return lhs.eqBase == rhs.eqBase
    }
}

然后您可以像这样使用它:

And then you can use it like this:

var a = FunctionCommand()
a.commands = [AnyCommandable(MoveCommand()), AnyCommandable(FunctionCommand())]

您可以轻松访问commands的属性,因为AnyCommandable实现了Commandable

And you can easily access properties of commands, because AnyCommandable implements Commandable

a.commands[0].condition

您需要记住现在将HashableEquatable添加到所有命令中. 我将这些实现用于测试:

You need to remember to now add Hashable and Equatable to all your commands. I used those implementations for testing:

struct MoveCommand: Commandable {

    var movingVector: CGVector!

    var condition: Condition?
    func execute() -> SKAction {
        return SKAction()
    }

    var hashValue: Int {
        return Int(movingVector.dx) * Int(movingVector.dy)
    }

    public static func ==(lhs: MoveCommand, rhs: MoveCommand) -> Bool {
        return lhs.movingVector == rhs.movingVector
    }
}

struct FunctionCommand: Commandable {
    var commands = [AnyCommandable]()

    var condition: Condition?

    func execute() -> SKAction {
        return SKAction.group(commands.map { $0.execute() })
    }

    var hashValue: Int {
        return commands.count
    }

    public static func ==(lhs: FunctionCommand, rhs: FunctionCommand) -> Bool {
        return lhs.commands == rhs.commands
    }
}

这篇关于协议类型数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆