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

问题描述:

我将从小代码示例开始,尽可能简化以总结问题。如何使用通用参数对函数执行“可选展开”

比方说,我已经得到了定义如下的功能:

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 
    //... 
] 

最后,我将遍历所有键/ f恩膏调用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手册的整个泛型页面,没有提示如何实现这一点。

请让我知道任何现有的方式来使用通用性执行此类事情。

+0

这些被称为高kinded类型。 Swift不支持它们。 – Alexander

+1

字典中的函数'f1','f2'和'f3'是否返回不同的类?他们至少能遵守同样的协议吗? – Dmitry

+0

@Dimitry不,他们不会从不同的类返回,实际上他们的参数之一是一个闭包,每一个参数都是一个不同的类型。 我想我必须让它们符合协议,谢谢 – dulgan

代替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不能在铸造中使用后虽然。

+0

我会给它一个镜头,但我不会我认为我可以在doSomething函数中用正确的参数调用函数。 我改变了我的代码以接近我真正的问题,请让我知道是否改变了某些东西 – dulgan

+0

我设法让它工作,因为我其实并不需要知道完成块中对象的类(见编辑的问题),包装想法+1 – dulgan

我终于做到了这种方式:

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 
    //... 

]