簡體   English   中英

使用struct / protocols進行Swift多態閉包調度

[英]Swift polymorphic closure dispatch with struct/protocols

我有一種情況,我想向服務注冊一個參數或沒有參數閉包。 總是有一個可用的參數,但是為了簡潔起見,我也希望不注冊任何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 happenedsomething 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 ),但即使我將typealias表達式放入擴展名中,它也告訴我T未使用。

是否可以用struct / protocol做到這一點? 我對枚舉解決方案感到滿意,但對使用struct / protocol方法無法實現它感到失望。

從您想要將結構var action: Action = UnaryAction {...}為協議的事實(通過使用var action: Action = UnaryAction {...} )來var action: Action = UnaryAction {...} ,我假設您不需要publish方法來在調用它時具有正確的簽名。

換句話說,通過使用typealias聲明您的Action協議,編譯器期望為結構的每個實例專門化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) )。 您還需要在執行關閉時考慮到這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM