在Swift中是否有一种简单明确的方法来检查某些东西是否是可调用的块/函数?在某些语言中,它是一件微不足道的事情,但也许我从Swift的错误角度看待这个问题?请考虑以下事项。
func foo(){ print("foo") }
var bar: () -> () = { print("bar") }
var baz: () -> (Bool) = { print("baz"); return true }
print(foo) // (Function)
print(bar) // (Function)
print(baz) // (Function)
print(foo is () -> ()) // true
print(bar is () -> ()) // true
print(baz is () -> ()) // false
print(baz is () -> (Bool)) // true
Swift知道它们都是函数,尽管没有这样的数据类型。我可以使用可靠的签名进行检查,但可能存在我不关心签名 * 而只是想调用它的情况。例如:
func call(callable: () -> ()) {
callable()
}
call(foo) // foo
call(bar) // bar
call(baz) // error: cannot convert value of type '() -> (Bool)' to expected argument type '() -> ()'
我可以像这样重写它,这适用于Void
和Bool
返回类型,但对每种类型都这样做很疯狂,特别是因为我不关心它,但是编译器确实......
func call(callable: Any) {
if let block: () -> () = callable as? () -> () {
block()
} else if let block: () -> (Bool) = callable as? () -> (Bool) {
block()
}
}
call(foo) // foo
call(bar) // bar
call(baz) // truely baz
* 同意,不关心签名是罪。为了论证,我们不关心返回类型。
答案 0 :(得分:4)
您可以检查可调用的.dynamicType
的字符串表示是否存在子字符串->
。不是超级优雅,但它有效:
func isAClosure<T>(foo: T) -> Bool {
return String(foo.dynamicType).containsString("->")
}
var a : () -> () = { print("Foobar") }
var b : (Double) -> (Bool) = { $0 > 0 }
var c : Int = 1
isAClosure(a) // true
isAClosure(b) // true
isAClosure(c) // false
当然,正如Marcus Rossel在上面的评论中指出的那样,你仍然不会知道关于可调用的参数的任何信息(但也许这可能是下一步要找出来的,因为你知道它&#39 ; sa callable)。
关于以下OP问题的补充:仅仅是技术讨论,而不是推荐的技术。
使用与上面相同的方法来检查函数参数是否是不带参数的闭包(() -> (...)
),或者既不带参数也不带返回类型(() -> ()
),等等。使用这种方法,您可以定义一个泛型函数,只有当它具有某种闭包类型时才调用发送给函数的参数。对于&#34;函数内调用&#34; ,您必须使用类型转换为预期的闭包类型,就像您在Q中所描述的那样以上。它可能很难规避这种非通用的&#34;接近w.r.t. 调用闭包。下面是一些例子。
/* Example functions */
func isAVoidParamClosure<T>(foo: T) -> Bool {
let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ")
return bar.count > 1 && (bar.first?.characters.count ?? 0) == 2
}
func callIfVoidVoidClosure<T>(foo: T) {
let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ")
if bar.count > 1 && !(bar.map{ $0 == "()" }.contains(false)) {
if let foo = foo as? () -> () {
foo()
}
}
}
func isASingleDoubleReturnTypeClosure<T>(foo: T) -> Bool {
let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ")
return bar.count > 1 && bar[1] == "Double"
/* rhs of '&&' lazily evaluated: [1] ok */
}
func printTwoTimesResultOfVoidDoubleClosure<T>(foo: T) {
if isAVoidParamClosure(foo) && isASingleDoubleReturnTypeClosure(foo) {
if let foo = foo as? () -> Double {
let a: Double = 2*foo()
print(a)
}
}
}
示例电话:
/* Example calls */
let a : () -> () = { print("Foobar") }
let b : (Double) -> (Bool) = { $0 > 0 }
let c : () -> Double = { 21.0 }
let d : Int = 1
isAVoidParamClosure(a) // true
isAVoidParamClosure(b) // false
isAVoidParamClosure(c) // true
isAVoidParamClosure(d) // false
callIfVoidVoidClosure(a) // Prints "Foobar"
callIfVoidVoidClosure(b)
callIfVoidVoidClosure(c)
callIfVoidVoidClosure(d)
printTwoTimesResultOfVoidDoubleClosure(a)
printTwoTimesResultOfVoidDoubleClosure(b) // Prints "42.0"
printTwoTimesResultOfVoidDoubleClosure(c)
printTwoTimesResultOfVoidDoubleClosure(d)