我有一种情况,我想向服务注册一个参数或无参数闭包。总是有一个可用的参数,但为了简洁起见,我希望能够注册没有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
表达式放在了扩展中。
用结构/协议可以做到这一点吗?我对enum解决方案很满意,但对无法用结构/协议方法实现它感到失望。
根据您想要将结构转换为它们的协议(通过使用var action: Action = UnaryAction {...}
)的事实判断,我假设您不需要publish
方法在调用它时具有正确的签名。
换句话说,通过用typealias
声明Action
协议,编译器期望为结构的每个实例专门化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)
)。您还需要在执行闭包时考虑到这一点。