简体   繁体   English

Swift:相等的泛型类型数组

[英]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上有关泛型的一些问题作斗争,但我找不到找到解决方法的方法:

  • I have a class 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的简单协议。
  • I have a class 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类型,我将需要它们扩展EquatableNSObject以便能够在两个之间比较状态他们。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM