如何在Swift中检查/判断函数类型?

3
例如:
func f(x: Int) -> Int {
    return x
}
func h(f: @escaping (Int) -> Any) { if (f is (Int) -> Int) { print(f(1)) } else { print("无效") } }
h(f: f)
我期望它打印出1,但实际上它打印出了无效

一种笨拙的方法:如果 f(1) 是 Int { - vacawama
我怀疑在所提供的闭包f中,Any包装的实例的类型无法被编译器推断出来,除非调用了f。在编译器的眼中,f是一个形式为{ arg in; /*...*/; return someInstanceOfTypeAny }的闭包。无论someInstanceOfTypeAny实际上是否为Int类型,只有通过实际研究由闭包f返回的实际实例的类型(例如如@vacawama上面建议的那样),才能确定。下面的答案使用模板解决方法,在每次调用h时,T实际上是一个具体类型而不是运行时包装! - dfrib
@dfri 当将 f 传递给 h 时,编译器知道 f 的类型是 (Int) -> Int,我想知道为什么它不能将 (Int) -> Any 缩小为子类型 (Int) -> Intf 不是没有类型注释的匿名闭包。f 已经被很好地注释了。 - weakish
@weakish 这里的 Any 不是一个类型占位符(它持有任何符合 Any 的类型),就像下面答案中使用的泛型类型占位符 T 一样。如果你尝试将你的示例中的 Any 替换为自定义类 Base,并将 func f (... 的返回类型替换为 Derived(派生自 Base),并测试提供的闭包是否为 (Int) -> Derived,你会发现当向 h 提供 (Int) -> Derived 闭包时,仍然会进入 "invalid" 主体,因为 h 中的参数 f 明确地被类型注释为 (Int) -> Base(即使提供了 (Int) -> Derived)。 - dfrib
3个回答

3

有一个使用泛型的解决方法:

func intF(x: Int) -> Int {
    return x
}

func stringF(x: Int) -> String {
    return "\(x)"
}

func h<T>(f: (Int) -> T) {
    if (T.self == Int.self) {
        print(f(1))
    } else {
        print("invalid")
    }
}

h(f: intF)    // prints: 1
h(f: stringF) // prints: invalid

1

使用 Any 几乎总是代码异味的表现,你应该尽可能地依赖 Swift 提供的类型安全性。通过使 h 成为泛型,在编译时进行验证,可以实现这一点。

// the overload that does the actual stuff
func h(f: @escaping (Int) -> Int) {
    print(f(1))
}

// this maps to all other types
func h<T>(f: @escaping (Int) -> T) {
    print("invalid")
}

h { _ in return "15" }  // Invalid
h {  2 * $0 }           // 2

“你甚至可以放弃通用重载,这样你就能免费获得编译检查,而不是运行时失败(更加可靠和预测)。 ”

我写了 Any,所以 (Int) -> Int(Int) -> Any 的子类型。这是为了让代码示例简短,而不需要定义很多类。 - weakish

0
你可以将 h 重写为一个通用函数:
func h<T>(f: @escaping (Int) -> T) {        
    if T.self == Int.self  {
        print(f(1))
    } else {
        print("invalid")
    }
}

但是更好的方法是为 h 编写特定类型的重载,并为其他情况编写一个通用的捕获(如果你确实需要的话)。


这与@nils的答案相同吗? - weakish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接