arrays - 如何使类属性数组成为泛型类型的异构数组?

标签 arrays swift generics

这就是我想要的。我正在写一个非常简单的 event dispatcher (单击该链接以查看我的代码)。当我只有 listen() 时它工作正常和 fire()方法。这是您可以使用它的方式:

struct UserHasBirthday: Event {
    let name: String

    init(name: String) {
        self.name = name
    }
}

let events = TestDispatcher()
events.listen {
    (event: UserHasBirthday) in
    print("Happy birthday \(event.name)!")
}
events.fire( UserHasBirthday(name: "John Doe") )

一切都很好,但现在我想添加一个功能,您可以将事件推送到队列中,然后一次性触发所有事件。这就是我添加 push 和 flush 方法的原因。

现在的问题是在flush()方法我需要能够向下转换通用 Event类型为给定的特定事件类型。否则 fire()方法无效。

所以我想,也许我可以将类型信息保存在与事件本身相同的数组中。如您所见,我试图用一个元组来做到这一点。不幸的是,它不是那样工作的。

我认为,如果我能找到一种方法来生成变量 pushedEvents接受像这样的通用类型:var pushedEvents = Array<E: Event>()然后它可以工作。但我知道这样做的唯一方法是像这样将泛型分配给整个类:class TestDispatcher<E: Event> { } ,但是该类的每个实例只能用于一种特定类型的事件,我绝对不希望那样。

有没有人知道某种方法可以使这项工作正常进行?

最佳答案

This guy on reddit通过使用所谓的类型删除模式(我不知道该模式)为我提供了解决方案。

我编辑了他的代码以更多地满足我的需求,这就是我现在拥有的:

public protocol Event {}

public protocol ErasedListener {
    func matches(eventType: Event.Type) -> Bool
    func dispatchIfMatches(event: Event)
}

public struct Listener<T: Event>: ErasedListener {
    let dispatch: T -> Void

    public func matches(eventType: Event.Type) -> Bool {
        return matches(String(eventType))
    }

    func matches(eventType: String) -> Bool {
        return eventType == String(T.self)
    }

    public func dispatchIfMatches(event: Event) {
        if matches(String(event.dynamicType)) {
            dispatch(event as! T)
        }
    }
}

public protocol Dispatcher {
    func listen<E: Event>(listener: E -> Void)
    func fire(event: Event)
    func queue<E: Event>(event: E)
    func flushQueueOf<E: Event>(eventType: E.Type)
    func flushQueue()
    func forgetListenersFor<E: Event>(event: E.Type)
    func emptyQueueOf<E: Event>(eventType: E.Type)
    func emptyQueue()
}

public class MyDispatcher: Dispatcher {
    var listeners = [ErasedListener]()
    var queuedEvents = [Event]()

    public init() {}

    public func listen<E: Event>(listener: E -> Void) {
        let concreteListener = Listener(dispatch: listener)

        listeners.append(concreteListener as ErasedListener)
    }

    public func fire(event: Event) {
        for listener in listeners {
            listener.dispatchIfMatches(event)
        }
    }

    public func queue<E: Event>(event: E) {
        queuedEvents.append(event)
    }

    public func flushQueue() {
        for event in queuedEvents {
            fire(event)
        }
        emptyQueue()
    }

    public func emptyQueue() {
        queuedEvents = []
    }

    public func flushQueueOf<E: Event>(eventType: E.Type) {
        for event in queuedEvents where String(event.dynamicType) == String(eventType) {
            fire(event)
        }
        emptyQueueOf(eventType)
    }

    public func forgetListenersFor<E: Event>(eventType: E.Type) {
        listeners = listeners.filter { !$0.matches(eventType) }
    }

    public func emptyQueueOf<E: Event>(eventType: E.Type) {
        queuedEvents = queuedEvents.filter { String($0.dynamicType) != String(eventType) }
    }
}

示例用法

struct UserDied: Event {
    var name: String
}

class UserWasBorn: Event {
    let year: Int

    init(year: Int) {
        self.year = year
    }
}

// you can use both classes and structs as events as you can see

let daveDied = UserDied(name: "Dave")
let bartWasBorn = UserWasBorn(year: 2000)

var events = MyDispatcher()

events.listen {
    (event: UserDied) in

    print(event.name)
}

events.listen {
    (event: UserWasBorn) in

    print(event.year)
}

events.queue(daveDied)
events.queue(UserWasBorn(year: 1990))
events.queue(UserWasBorn(year: 2013))
events.queue(UserDied(name: "Evert"))

// nothing is fired yet, do whatever you need to do first

events.flushQueue()
/* 
    This prints:
    Dave
    1990
    2013
    Evert
*/

// You could also have flushed just one type of event, like so:
events.flushQueueOf(UserDied)
// This would've printed Dave and Evert,
// but not the year-numbers of the other events

关于arrays - 如何使类属性数组成为泛型类型的异构数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36241713/

相关文章:

json - Swift 条件符合两种情况

arrays - Swift:访问字典数组中的数组值

c# - 如何解析包含引号的字符串

ios - 聊天应用程序中彩色背景的奇怪形状

java - 反射(reflect)具有泛型类型参数的方法时出现 NoSuchMethodException

java - 使用 Java 泛型有界类型参数

c# - 包含方法的数组

C - 如何将数组中的任意组合 char 或 int 连接到 int 或 char 类型的二进制字符串

ios - Swift - UIView 绘制 1 像素宽的线

ios - 如何在不为可选参数指定值的情况下快速调用以下函数?