在Swift中是否可以创建通用闭包?

41
func myfunc<T>(i:T) -> T {
    return i
}

这个通用函数可以成为闭包吗?

let myfunc = { <T>(i:T) -> T in
    return i
}

这个不起作用...


1
类似(相同?)的问题在这里:https://dev59.com/eV8e5IYBdhLWcg3wwMlW。 - Martin R
1
泛型不能与闭包一起使用。@MartinR提供的答案提供了一种解决方法。我认为这个问题不是重复的,因为另一个问题是关于使带有泛型的闭包工作,而这个问题明确询问是否可以使用泛型。 - Antonio
我认为答案是这样的,这不起作用是因为它实际上没有意义(与特定的Swift无关)。myfunc将是一个抽象类型,这与尝试构造抽象类相同。我在@MartinR链接的问题的答案中讨论更多。 - Rob Napier
4个回答

25
不行,因为变量和表达式不能是通用的。只有通用函数和通用类型。
澄清一下:在某些语言中,你可以拥有带有全称量词的类型,比如 forall a. a -> a。但在 Swift 中,类型不能有全称量词。所以表达式和值本身不能是通用的。函数声明和类型声明可以是通用的,但当你使用这样的通用函数或者通用类型的实例时,某种类型(它可以是一个真实类型或者一个类型变量)会被选择作为类型参数,之后你得到的值不再是通用的了。

1
我同意Rob的观点,即在使用闭包定义通用闭包类型时,应该考虑使用typealias - Mazyod
在大多数编程语言中,通用函数参数实际上只是在编译时嵌入值的函数参数。这意味着通用参数的可能值在编译时进行评估,而不仅仅是在运行时使用(就像普通函数/闭包参数一样)。优点是能够将需要在编译时评估的类型参数化闭包,例如StringLiteralConvertible。我认为你的困惑在于将变量或表达式视为通用;相反,它是通用的闭包。 - Slipp D. Thompson
这个怎么样? typealias ResultClosure<T> = (ResultCode, String?, T?) -> Voidfunc loginUser(userName: String, password: String, resultHandler: ResultClosure?)它是泛型的,并且具有编译时类型检查。 - Konstantin Khamenok
在 C++ 中允许变量具有泛型。 - Sapphire_Brick

12
也许您需要像这样的东西。
类型声明:
typealias ResultClosure<T> = (ResultCode, String?, T?) -> Void

函数声明:

func loginUser(userName: String, password: String, resultHandler: ResultClosure<TokenModel>?)

使用方法:

    NetConnector.shared.loginUser(userName: userName ?? "", password: password ?? "") { (code, message, data) in
        self.display?.unlockScreen()
        if code == .success {
            if let activeToken = data {
                AppData.shared.userToken = activeToken
            }
            self.display?.showHome()
        } else {
            self.display?.showError(errorMessage: message)
        }
    }

1
不错,但是使用类型约束和定义一个带有该类型的对象属性(typealias),它将无法工作。 - OhadM

4

如前所述,Swift 中的变量不能是通用的,因此创建一个闭包,其通用类型由调用者指定是不可能的。但是,有一些解决方法:

通过 SE-253,可以使任意(名义)类型可调用。因此,我们可以声明一个(非通用的)结构体,该结构体具有通用的callAsFunction方法,而不是声明一个通用的闭包:

struct MyFunc {
    func callAsFunction<T>(_ i: T) -> T {
        return i
    }
}

现在,我们可以声明一个非泛型变量,并使用泛型值进行调用:
let myFunc = MyFunc()
let x = myFunc(42) // -> Int
let y = myFunc("foo") // -> String

请注意,这种解决方法并不适用于所有情况,但在某些情况下可能会有所帮助。

0

我已经找到了一些替代方案,你可以在闭包中使用 Anyobject,并将任何值传递给你的方法。

typealias genericCompletion<T:AnyObject> = ((Bool,T,String) -> Void)
struct Student {
    var name:String = "Kishore"
    var age : String = "125"
}
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.createAGenericReturn { (success, object, message) in

        }

        self.createStructGeneric { (success, student, message) in

        }

    }


    func createAGenericReturn(callback:@escaping(genericCompletion<AnyObject>)){
        callback(true,434.433 as AnyObject,"kishoreTest")
    }

    func createStructGeneric(callback:@escaping(genericCompletion<AnyObject>)){
        callback(true,Student.init() as AnyObject,"kishoreTest")
    }

}

在这里你可以看到我将“泛型”称为 Anyobject typealias genericCompletion = ((Bool,T,String) -> Void),所以你可以向其中传递任何值。


有趣的解决方案,利用多态性。然而,这会削弱类型检查器,而且需要实现通用闭包的代码如果想支持多种类型,就必须进行大量的下转换。 - Cristik

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