[英]Swift: Array of equatable generic type
I have been the last days fighting with some issues regarding generics on Swift, and I don't find a way to figure out how to achieve this: 我是在最后几天与Swift上有关泛型的一些问题作斗争,但我找不到找到解决方法的方法:
class Store<S: State>
where State
is a simple protocol which extends Equatable protocol State: Equatable
. class Store<S: State>
,其中State
是扩展Equatable protocol State: Equatable
的简单协议。 Logger
where I want to store an array of Stores
to keep track of each change on them and compare their State
with their old values, being able to check what changes in each iteration. Logger
类,我想在其中存储一组Stores
来跟踪它们的每个更改,并将它们的State
与它们的旧值进行比较,从而能够检查每次迭代中的更改。 For this, I need to store an array of Any Kind of Store in my logger class. 为此,我需要在logger类中存储任何类型的存储数组。 The problem comes when I try to use a
val storeArray = [Store<Any>]
, it doesn't work because Any is not an Equatable
type, and I will need them to extend Equatable
or NSObject
to be able to compare the states between them. 当我尝试使用
val storeArray = [Store<Any>]
,问题就来了,这是行不通的,因为Any不是Equatable
类型,我将需要它们扩展Equatable
或NSObject
以便能够在两个之间比较状态他们。
Is possible to achieve this in Swift? 可以在Swift中实现吗? Or find out another way to compare 2 items without make a generic extend a
Equatable
protocol? 还是找到另一种比较两个项目而不使泛型扩展
Equatable
协议的方法?
In case you wanna check the implementation : 如果您想检查实现:
State : 状态:
protocol State: Equatable {
}
Store : 店铺:
class Store<S: State> {
private var _initialState: S
private var _state: S
private var processor = PublishSubject<S>()
init(initialState: S) {
_initialState = initialState
_state = initialState
}
var state: S {
get {
return _state
}
set(value) {
if (value != state) {
_state = value
processor.onNext(value)
}
}
}
func initialState() -> S {
return _initialState
}
/// Initialize the store. Called after all stores instances are ready.
func initialize() {
//TODO Check if its possible to force an override over a method
preconditionFailure("This method must be overridden")
}
}
After the suggestion of Vadian I have tried to move it to a protocol with an associate type: 根据Vadian的建议,我尝试将其移至关联类型的协议中:
protocol Store: class {
associatedtype State : StateDelegate
var processor : PublishSubject<State> { get }
var _state : State { get set }
var state: State { get set }
func initialState() -> State
func flowable() -> Observable<State>
func initialize() -> Void
}
extension Store {
var state: State {
get {
return _state
}
set(value) {
if (value != state) {
_state = value
processor.onNext(value)
}
}
}
func flowable() -> Observable<State> {
return processor.startWith(state)
}
func initialState() -> State {
return State.init()
}
}
But I retrieve the next error when I try to create an [Store]
array: Protocol 'Store' can only be used as a generic constraint because it has Self or associated type requirements
但是,当我尝试创建
[Store]
数组时,我检索了下一个错误: Protocol 'Store' can only be used as a generic constraint because it has Self or associated type requirements
I think I understand your problem now. 我想我现在明白你的问题了。 How about this solution:
该解决方案如何:
Instead of letting State
conform to Equatable
, you add your own custom equality check to the protocol; 不必让
State
符合Equatable
,而是向协议添加自己的自定义相等性检查; this way you can store your states in an array ( var states: [State]
). 这样,您可以将状态存储在数组中(
var states: [State]
)。 The drawback is that you cannot use generics but instead have to type-check in code, like in the good old times. 缺点是您不能使用泛型,而必须像过去那样在类型中检入代码。
For example, a simple version of the State
protocol: 例如,
State
协议的简单版本:
protocol State {
func isEqualTo(_ other: State) -> Bool
}
Your concrete state types have to implement isEqualTo
and perform a type check before testing equality: 您的具体状态类型必须实现
isEqualTo
并在测试相等性之前执行类型检查:
struct State1: State {
var foo: String
func isEqualTo(_ other: State) -> Bool {
guard let state1 = other as? State1 else { return false }
return self.foo == state1.foo
}
}
Now you can store your states in an array and eg check if a new state already is contained: 现在,您可以将状态存储在数组中,例如,检查是否已包含新状态:
let states: [State] = [ State1(foo: "hi"), State2(bar: 42), State1(foo: "bye")]
let newState = State2(bar: 42)
let containsNewState = states.contains { $0.isEqualTo(newState )}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.