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

Arrays 如何使类属性数组成为泛型类型的异构数组?,arrays,swift,generics,Arrays,Swift,Generics,这是我想要的。我正在编写一个非常简单的(单击该链接查看我的代码)。当我只有listen()和fire()方法时,它工作得很好。这是您可以使用它的方式: struct UserHasBirthday: Event { let name: String init(name: String) { self.name = name } } let events = TestDispatcher() events.listen { (event: Use

这是我想要的。我正在编写一个非常简单的(单击该链接查看我的代码)。当我只有
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()
方法中,我需要能够将泛型
事件
类型向下转换为给定的特定事件类型。否则,
fire()
方法将无法工作

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

我想如果我能找到一种方法,让变量
pushedEvents
接受这样一种泛型类型:
var pushedEvents=Array()
,那么它就可以工作了。但我知道的唯一方法是将泛型分配给整个类,就像这样:
classtestdispatcher{}
,但是该类的每个实例只能用于一种特定类型的事件,我绝对不希望这样


有人知道这种方法吗?

问题是Swift不允许将类型转换为元类型

一种解决方法是在
TestDispatcher
类的
flush()
函数的开关盒中包含符合
Event
的所有类型(至少是您将在
Dispatcher
中使用的类型)。它的功能不如我相信您想要的那样通用,而类型擦除是实现这一点的方法。然而,我将保留我的原始答案,因为它解释了为什么您最初尝试转换为元类型的方法不起作用

public protocol Event {}

public enum Listener<E: Event> {
    public typealias T = E -> ()
}

public protocol Dispatcher {
    func listen<E: Event>(listener: Listener<E>.T)
    func fire<E: Event>(event: E)
    func push<E: Event>(event: E)
    func flush()
}
通过使用所谓的类型擦除模式(我不知道该模式)为我提供了解决方案

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

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) }
    }
}

谢谢你的回复。这对我来说并没有什么好处,因为如果事件调度器仅限于几个事件,那么它对我来说就有点无用了。在这种情况下,我宁愿不使用推/刷新功能。我投了你一票,因为也许真的没有别的办法了。但我还没有将你的答案标记为已被接受,因为我仍然抱着一些希望,希望这可以通过某种黑客的方式实现。就像我使用枚举创建泛型typealias一样。@Evert我能理解,而且我也认为你应该把这个问题保留一天左右,也许其他人可以提供更有价值的黑客输入(我使用了“可能不会比这更好”这个词)如上所述,实际上可能存在一些我没有考虑过的结构包装解决方法:)。如果您感兴趣,我已经发布了一个有效的答案。@Evert:不错,使用类型擦除确实在我昨天的脑海中出现过,但我对这个概念不是很熟悉,所以我放弃了它。此外,擦除类型使用的每个附加方法都会产生一些样板文件,但在这种情况下,我认为这可能是最好的解决方案,干得好(对您和reddit guy:)。如果可能的话,在你的答案中添加一个用法示例(如:仅侦听器框架),然后我认为你应该接受它作为回答你的问题。它说“你可以明天接受你的答案”。如果我记得的话,我会这么做的。
struct UserHasBirthday: Event {
    let name: String

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

struct UserWonTheLottery: Event {
    let name: String
    let amount: Int

    init(name: String, amount: Int) {
        self.name = name
        self.amount = amount
    }
}

let events = TestDispatcher()
events.listen {
    (event: UserHasBirthday) in
    print("Happy birthday \(event.name)!")
}
events.listen {
    (event: UserWonTheLottery) in
    print("Congratulations \(event.name) for winning \(event.amount)!")
}

events.push(UserHasBirthday(name: "John Doe"))
events.push(UserHasBirthday(name: "Jane Doe"))
events.push(UserWonTheLottery(name: "Jane Doe", amount: 42000))

events.flush()
/* Happy birthday John Doe!
   Happy birthday Jane Doe!
   Congratulations Jane Doe for winning 42000! */
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