Thanks for clarifying further!
I think I see what you're going for there. I'm going to try an clean up your code to get it as close to running as possible, and then go from there. Let me know if I've made any assumptions that were incorrect.
Assumption: From class CollectionModel<Model>
it looks like you're trying to create a collection of things that conform to BaseEvent<M>
, where M
is the same type for all events. For example, [AddEvent<Int>, RemoveEvent<Int>]
, would be a valid collection, but [AddEvent<Int>, RemoveEvent<Float>]
would not be valid.
If that's the case then what you were likely going for with your cast
method was something more like
func cast<Container: BaseEvent<Model>>(event: BaseEvent<Model>) -> Container? {
return event as? Container // If casting fails, will return nil
}
This would be a method that takes a BaseEvent<Model>
and returns another type of event if it can cast to it.
This compiles, but we end up running into the same problem as before
func genericHandler<Event: BaseEvent<Model>>(event: Event) {
// Desired way to accomplish this
if let castEvent = cast(event: event) {
handleEvent(event: castEvent) // prints "ignore base event"
}
}
unless you provide a more specific type to attempt to cast to, cast(event:)
will always default to returning a BaseEvent<>
func genericHandler<Event: BaseEvent<Model>>(event: Event) {
// Desired way to accomplish this
if let castEvent: AddEvent<Model> = cast(event: event) { // Change here
handleEvent(event: castEvent) // prints "add to collection"
}
}
So not a great position to be in. If you find yourself casting a ton, it usually means you're fighting the type system and there's often a better way. In this case, one thing you could do is flip everything and insert the effects of each event in the event itself.
class CollectionModel<Model> {
func genericHandler<Event: Handlable>(event: Event) {
event.handle()
}
}
protocol Handleable {
func handle() // handle placed into a protocol for cleanliness, but you could also place it into BaseEvent if you'd like
}
class BaseEvent<M>: Handlable {
func handle() {
print("ignore base event")
}
}
class AddEvent<M>: BaseEvent<M> {
override func handle() {
print("add to collection")
}
}
class RemoveEvent<M>: BaseEvent<M> {
override func handle() {
print("remove from collection")
}
}
let collection = CollectionModel<Int>()
let addEvent = AddEvent<Int>()
let secretAddEvent: BaseEvent<Int> = AddEvent<Int>()
collection.genericHandler(event: secretAddEvent) // prints "add to collection"
I feel that this works better than your initial approach. The effects of each event are contained within the event itself, which is nice for a few OOP related reasons. Casting can get pretty messy so avoiding it completely reduces potential bugs.
Let me know if you have any questions or if I've misunderstood 