Быстрая отправка полиморфного замыкания со структурой / протоколами

У меня есть случай, когда я хочу зарегистрировать либо один аргумент, либо отсутствие закрытия аргумента в службе. Всегда есть доступный аргумент, но для краткости я хочу также иметь возможность регистрировать не закрытия 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 метод для каждого экземпляра ваших структур.

Это означает, что у вас есть два варианта:

  1. Удалить тип литья:

Пример:

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

  1. Сделать протокол не универсальным

Пример:

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)). Вы также должны учитывать это при выполнении закрытия.

Другие вопросы по тегам