在iOS Swift中重载通用函数

问题描述 投票:1回答:1

我试图建立一个finder,它可以尝试并找到在一个闭包中传递给它的多个类型。

enum SomeError: Error {
    case notInitialized
}

struct TestFinder {

    func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U {
        guard let t: T = get() else {
                throw SomeError.notInitialized
        }

        return function(t)
    }

    func getSomething<T, U, V>(_ function: @escaping (T, U) -> V) throws -> V {
        guard let t: T = get(), let u: U = get() else {
                throw SomeError.notInitialized
        }

        return function(t, u)
    }

    func getSomething<T, U, V, W>(_ function: @escaping (T, U, V) -> W) throws -> W {
        guard let t: T = get(), let u: U = get(), let v: V = get() else {
                throw SomeError.notInitialized
        }

        return function(t, u, v)
    }

    private func get<T>() -> T? {
       nil
    }
}

struct UserDetails {
    let name: String
    let roll: String
}

我将finder调用为。

let testReturnType = try? TestFinder().getSomething(UserDetails.init)

编译器向我抛出一个错误:

"getSomething "的使用有误。

这个错误的原因(来自文档)。

你可以通过在类型参数上提供不同的约束、要求或两者都提供来重载一个通用函数或初始化器。当你调用一个重载的泛型函数或初始化器时,编译器会使用这些约束来解决要调用哪个重载函数或初始化器。

但如果我注释。

func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U

一切都开始工作了 这与编译器无法识别解析哪个函数签名有关。

有什么特别的解决方法吗?

ios swift generics closures overloading
1个回答
1
投票

你还没有完全集中在实际问题上。让我们从这个例子中删除所有不相关的东西。这样编译后就能正常工作了。

struct TestFinder {

    func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
        print("two")
    }

    func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
        print("three")
    }

    func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
        print("four")
    }

}

我们在这里测试一下

    func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
    TestFinder().doSomething(f) // "four"

但如果你在版本中加入 一个 传递的函数参数,一切都崩溃了。

struct TestFinder {

    func doSomething<T>(_ function: (T) -> Void) -> Void {
        print("one")
    }

    func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
        print("two")
    }

    func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
        print("three")
    }

    func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
        print("four")
    }
}

现在我们不能编译了,因为... 第一 版本被视为候选人。而事实上,如果我们把 其他 版本,我们 编译!

struct TestFinder {

    func doSomething<T>(_ function: (T) -> Void) -> Void {
        print("one")
    }

}

这就是奇怪的地方。我们仍然在编译,即使我们在说。

    func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
    TestFinder().doSomething(f)

很明显,这个有四个参数的函数 被编译器认为是 "适合 "声明中的 "适合"。一个 通用参数。

我认为这是一个bug。我想我可以猜到是什么原因造成的,可能与函数参数列表为元组的传统有关。这个函数 f 是 "等同于 "一个函数取一个 单一 参数由一个四串元组组成。然而,你实际上不能 召唤function 里面 doSomething 的四串元组;我根本找不到调用它的方法。

所以,我想说的是,把这看作是一个bug,暂时通过移除 第一 版本的通用。


更新。 在Swift团队的建议下,我用2020年5月4日的Swift 5.3开发工具链进行了测试。使用它,你的代码编译和行为与预期一致。这确实是一个bug,并且它已经被修复为下列问题的一部分

https:/bugs.swift.orgbrowseSR-8563。

先回到我的版本,我的代码也是如此,编译和行为都符合预期,四个版本的 doSomething 的存在。但是,请注意,如果您删除了除第一个版本以外的所有的 doSomething它仍然可以编译和运行。此外,你可以调用 function 通过将四个参数捆绑成一个元组并强制铸造,就像这样。

struct TestFinder2 {

    func doSomething<T>(_ function: (T) -> Void) -> Void {
        print("one")
        function(("manny", "moe", "jack", "henry") as! T)
    }

}

这似乎证实了我的猜测 你所看到的是一个函数参数列表中隐藏的元组性质的结果。我们可以从关于这个bug的讨论中得出同样的结论,其中提到了 "元组-splatting"。


-1
投票

关键点是UserDetails结构,因为这个结构有两个属性,而且没有任何的 设计的初始化器,初始化器可以是UserDetails(name:,roll:)或UserDetails(name:)或UserDetails(roll:),这是雄心勃勃的部分,如果你只是删除UserDetails的一个属性也可以,因为一个属性结构只有一个设计的初始化器。

如果你评论

func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U

也就是说,寻人者只有一个选择。

func getSomething<T,U,V>(_ function: @escaping(T,U) -> V) throws -> V
© www.soinside.com 2019 - 2024. All rights reserved.