2015-12-10 31 views
0

我有一种情况,我想注册一个参数或无参数闭包与服务。总是有一个可用的参数,但为了简洁起见,我希望能够不注册arg闭包,然后在没有可用参数的情况下分派闭包。从一个强大的面向对象的动态类型的背景,我们爱态分派和类继承的树木,让类型图自己出的到来,我可以扔在一起以下几点:与结构/协议的Swift多态闭包调度

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) 

所以我看到后面something happened在我的控制台42 。大。

但我想探索与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方法无法实现它感到失望。

+0

Swift中的协议和泛型可能非常棘手。也许这个线程的答案可以帮助你:http://milen.me/writings/swift-generic-protocols/(另请参见forums.developer.apple.com/thread/7350)。然而,这两者都与一些协议函数的泛型返回类型有关,但也许你可以使用动态类型的魔法来解决某些问题:)我有兴趣知道结果,对于您的具体示例(尝试修复某些内容但失败相当悲惨......) – dfri

回答

0

根据您希望将结构投射到其协议(通过使用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)

  • 使协议非通用
  • 实施例:

    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))。您还需要在执行关闭时进行说明。