如何使用通用参数对函数执行“可选的展开”

时间:2017-02-22 17:11:41

标签: swift generics

我将从小代码示例开始,尽可能简单地总结问题。

假设我有一个定义如下的函数:

func doSomething<T>(using f: (String, (T) -> Void) -> Void, key: String) {
   f("test", {
      object in
      // do something with object using key
      //...
   })

}

我有一堆带有类方法的自动生成的类,例如:

class MyClass {
   var prop1: String = ""
}

class MyClassAPI {

   class func f1(param: String, completion: (MyClass) -> Void) {
      let mc = MyClass()
      mc.prop1 = "hello"
      completion(mc)
   }
}

然后,我在字典中得到了一整套关键/函数对:

// Any because of cast problem !
let keyFunctions: [String: Any] = [ 
    "first" : MyClassAPI.f1,
    "second" : MySecondClassAPI.f2,
    "third" : MyThirdClassAPI.f3
    //...
]

最后,我将遍历所有键/函数来调用doSomething

for (k, f) in keyFunctions {
   // This line doesn't work as the compiler complain about typing errors
   doSomething(using: f, key: k)
}

我遇到的问题是我无法将我的函数转换为正确的类型以将它们传递给doSomething

Xcode假设使用f as! (String, (_) -> ()) -> Void强制转换然后给我一个错误,_不是一个类型。

我试图在没有机会的情况下使用if let uf = f as? (String, (Any) -> Void) -> Void更加宽容。

我阅读了Swift手册的所有泛型页面,没有任何关于如何实现这一点的提示。

请告诉我使用通用性执行此类操作的任何现有方法。

2 个答案:

答案 0 :(得分:1)

而不是Any为块创建更具体的类型

let keyFunctions: [String: (String) -> Any]

然后,至少,您将编译并调用doSomething。 但是,将其设为通用是没有意义的,因为T始终为Any。如果doSomething依赖于类之间的共同行为,那么为所有类定义协议是有意义的。

如果您确实希望获得有关您的类的信息,那么您可以引入一个非通用类来保留此信息:

class Wrap {
    var resultType: Any.Type
    let f: (String) -> Any

    init<T>(_ f: @escaping (String) -> T) {
        self.f = f
        resultType = T.self
    }
}

let keyFunctions: [String: Wrap] = [
    "first" : Wrap(MyClassAPI.f1),
    "second" : Wrap(MySecondClassAPI.f2)
]

但是resultType之后不能用于投射。

答案 1 :(得分:0)

我终于这样做了:

func doSomething<T>(using f: (String, (Any) -> Void, key: String, type: T.Type) -> Void, key: String) {
   f("test", {
      object in
      guard let o as? type else { return }
      // do something with o using key
      //...
   })
}

let keyFunctions: [String: (String, (Any) -> Void)] = [ 
   "first" : MyClassAPI.f1,
   "second" : MySecondClassAPI.f2,
   "third" : MyThirdClassAPI.f3
   //...