检查变量在Swift中是否为块/函数/可调用

检查变量在Swift中是否为块/函数/可调用,swift,Swift,在Swift中是否有一种简单明确的方法来检查某个东西是否是可调用的块/函数?在某些语言中,这是一件小事,但也许我在Swift中从错误的角度看待这件事?请考虑以下内容: func foo(){ print("foo") } var bar: () -> () = { print("bar") } var baz: () -> (Bool) = { print("baz"); return true } print(foo) // (Function) print(bar) // (F

在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

*同意,不在乎签名是一种罪恶。为了参数起见,我们不必关心返回类型。

您可以检查可调用的
.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
电话示例:

/* 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)

我想一个需要考虑的问题是,如果变量是可调用的,但如果它需要参数,则不必知道。如果你不知道某个东西的参数,那么知道它是否可调用是没有价值的。是的,因此有脚注。但我不是在谈论返回类型。我说的是参数。函数签名定义了所有这些,如果不清楚,很抱歉。你的想法是完全正确的,如果我们可以进行随机调用,如果可以很容易地导致更大的问题。谢谢,我考虑过类似的事情,就像你提到的,它只是感觉有点黑客。比如说,假设我们发现我们有一个不带参数的闭包。有没有一种方法可以在不知道返回类型的情况下调用它?或者我们还是必须像问题的最后一个例子那样进行转换?@IanBytchek我不确定我是否完全理解您想要做什么,但是:首先,我们不必使用返回类型,因此对于
让b:()->(Bool)={return true}
我们可以调用它并忽略返回类型,即
b()
。但是,如果您指的是在上面的函数中调用
foo
(如果我们知道这是一个闭包),则会更加棘手:编译器对
foo
的类型一无所知(即使我们知道)。此外,由于闭包是非标称类型,我们不能使用可能的替代方法,即键入约束某些函数以仅接受零参数闭包。仅比
foo短一点点。dynamicType
将是
t.self
。即使在实践中忽略返回值,为了让Swift调用函数/闭包,它必须具备参数和返回类型的静态知识。因此,正如在评论和回答中多次提到的,动态反省一个值是否是“可调用类型”没有什么价值。(甚至是类型签名的动态内省,因为您不能动态地使用它们。)
/* 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)