2

サービスに引数クロージャを 1 つ登録するか、引数クロージャを登録しない場合があります。利用可能な引数は常にありますが、簡潔にするために、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しかし、 and/orを使用してこれを行うことを検討したいと思い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ました。

構造体/プロトコルでこれを行うことは可能ですか? 列挙型ソリューションには満足していますが、構造体/プロトコルのアプローチでは実現できなかったことに失望しました。

4

1 に答える 1

0

(を使用して) 構造体をプロトコルにキャストしたいという事実から判断すると、呼び出し時にメソッドが正しい署名を持つvar action: Action = UnaryAction {...}必要はないと思います。publish

Actionつまり、プロトコルを で宣言することによりtypealias、コンパイラはpublish、構造体の各インスタンスのメソッドを特殊化することを期待しています。

これは、次の 2 つのオプションがあることを意味します。

  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)) を持つことになります。また、閉鎖の実行時にそれを考慮する必要があります。

于 2015-12-19T16:01:21.350 に答える