协议类型数组 [英] Array of protocol type
问题描述
我在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
您需要记住现在将Hashable
和Equatable
添加到所有命令中.
我将这些实现用于测试:
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屋!