在Swift

时间:2016-01-27 19:48:57

标签: swift

在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 '() -> ()'

我可以像这样重写它,这适用于VoidBool返回类型,但对每种类型都这样做很疯狂,特别是因为我不关心它,但是编译器确实......

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

* 同意,不关心签名是罪。为了论证,我们不关心返回类型。

1 个答案:

答案 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)