Быстрая отправка полиморфного замыкания со структурой / протоколами
У меня есть случай, когда я хочу зарегистрировать либо один аргумент, либо отсутствие закрытия аргумента в службе. Всегда есть доступный аргумент, но для краткости я хочу также иметь возможность регистрировать не закрытия arg, а затем просто отправлять замыкание без доступного аргумента в этом случае. Исходя из сильного OO и фона динамических типов, где мы любим полиморфную диспетчеризацию и деревья наследования классов и позволяем типам разобраться в себе, я могу добавить следующее:
class AbstractAction<T> {
func publish(value:T) {
fatalError("you should override this")
}
}
class NullaryAction<T>: AbstractAction<T> {
var closure:() -> ()
override func publish(_:T) {
closure()
}
init(closure:()->()) {
self.closure = closure
}
}
class UnaryAction<T>: AbstractAction<T> {
var closure:(T) -> ()
override func publish(value:T) {
closure(value)
}
init(closure:(T)->()) {
self.closure = closure
}
}
var action:AbstractAction = UnaryAction<Int>(closure: { print("\($0)") })
action.publish(42)
action = NullaryAction<Int>(closure: { print("something happened") } )
action.publish(42)
Итак, я вижу 42
с последующим something happened
в моей консоли. Отлично.
Но я хотел бы изучить возможность сделать это с struct
и / или enum
, Семантика значения - все в моде. enum
Подход был относительно простым, я думаю:
enum Action<T> {
case Nullary( ()->() )
case Unary( (T)->() )
func publish(value:T) {
switch self {
case .Nullary(let closure):
closure()
case .Unary(let closure):
closure(value)
}
}
}
var action = Action.Unary({ (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = Action<Int>.Unary( { print("shorthand too \($0)") } )
action.publish(42)
action = Action<Int>.Nullary({ print("something happened") })
action.publish(42)
Сделать struct
подход, я понимаю, что я должен использовать протокол для захвата общего интерфейса publish(value:T)
, Но вот где все путается, потому что протоколы, очевидно, нельзя смешивать с генериками? Я старался:
struct NullaryAction<T> {
typealias ValueType = T
var closure:() -> ()
}
struct UnaryAction<T> {
typealias ValueType = T
var closure:(T) -> ()
}
protocol Action {
typealias ValueType
func publish(value:ValueType)
}
extension NullaryAction: Action {
func publish(_:ValueType) {
self.closure()
}
}
extension UnaryAction: Action {
func publish(value:ValueType) {
self.closure(value)
}
}
var action:Action = UnaryAction(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
action = NullaryAction<Int>(closure:{ print("something happened") })
action.publish(42)
Это просто производит много ошибок внизу. Я пытался сделать расширения как обобщенные (например, extension NullaryAction<T>:Action
), но он сказал мне, что T
не использовался, хотя я поместил typealias
выражения в расширениях.
Возможно ли это сделать с помощью struct/protocol? Я доволен решением enum, но был разочарован тем, что не смог реализовать его с помощью подхода struct/protocol.
1 ответ
Судя по тому, что вы хотите привести свои структуры к их протоколам (используя var action: Action = UnaryAction {...}
Я полагаю, вам не нужно publish
способ иметь правильную подпись при ее вызове.
Другими словами, объявив Action
протокол с typealias
, компилятор рассчитывает специализировать publish
метод для каждого экземпляра ваших структур.
Это означает, что у вас есть два варианта:
- Удалить тип литья:
Пример:
var action /*removed the :Action type casting */ = UnaryAction<Int>(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
var anotherAction = NullaryAction<Int>(closure:{ print("something happened") }) //need to use another variable for this last one
anotherAction.publish(42)
Это решение делает ваш publish
методы также имеют ту же подпись, что и ваши структуры. Если ваша структура специализируется на работе с Ints
тогда у вас будет .publish(value: Int)
,
- Сделать протокол не универсальным
Пример:
protocol Action {
func publish(value:Any)
}
struct NullaryAction<T>: Action {
let closure: () -> ()
init(closure: () -> ()) {
self.closure = closure
}
func publish(value:Any) {
self.closure()
}
}
struct UnaryAction<T>: Action {
let closure: (T) -> ()
init(closure: (T) -> ()) {
self.closure = closure
}
func publish(value:Any) {
self.closure(value as! T) //Need to type cast here
}
}
var action: Action = UnaryAction<Int>(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
action = NullaryAction<Int>(closure:{ print("something happened") })
action.publish(42)
Это решение позволяет вам продолжать набирать текст, но ваш publish
методы будут иметь одинаковую подпись (.publish(value: Any)
). Вы также должны учитывать это при выполнении закрытия.