【问题标题】:Generic method override not working in swift通用方法覆盖不能快速工作
【发布时间】:2016-02-16 11:45:38
【问题描述】:

有一个协议 Printable 和一个来自第 3 方的 struct Printer。

protocol Printable {}

struct Printer {

    static func print<T>(object: T) -> String {
        return "T"
    }

    static func print<T: Printable>(object: T) -> String {
        return "Printable"
    }

}

现在我正在制作一个通用的

struct Generic<T> {

    var args: T

    func display() {
        print(Printer.print(args))
    }

}

还有两个结构体

struct Obj {}
struct PrintableObj: Printable {}
var obj = Generic(args: Obj())
var printableObj = Generic(args: PrintableObj())

当我调用它们的显示函数时。

obj.display()

显示 T

printableObj.display()

显示 T 但我希望它打印 "Printable"

我能想到的一个解决方案是使用两种不同的泛型

struct Generic<T>
struct PrintableGeneric<T: Printable>

在不改变 Printable 协议和 Printer 结构的情况下有没有其他解决方案。

【问题讨论】:

    标签: swift generics swift2


    【解决方案1】:
    static func print<T>(object: T) -> String {
        if object is Printable {
            return "Printable"
        } else {
            return "T"
        }
    }
    

    【讨论】:

    • 谢谢。但是该代码来自另一个来源,我无法修改它。我只能在 Generic 中进行更改。
    【解决方案2】:

    是的。但答案有点奇怪。第一部分很有意义;第二部分非常奇怪。让我们来看看吧。

    struct Generic<T> {
        var args: T
        func display() {
            print(Printer.print(args))
        }
    }
    

    print 选择的正确重载是在编译时而不是运行时决定的。这是最让人们困惑的事情。他们希望将 Swift 视为一切都是动态的 JavaScript。 Swift 喜欢是静态的,因为这样它可以确保你的类型是正确的并且它可以做很多优化(而且 Swift loves 做编译器优化)。那么,编译时,args 是什么类型?好吧,它是TT 是否已知为 Printable?不它不是。所以它使用非Printable 版本。

    但是当 Swift 使用 PrintableObj 专门化 Generic 时,它不知道它是 Printable 吗?那时编译器不能创建不同版本的display 吗?是的,如果我们在编译时知道这个函数的每个调用者,并且它们都不会被扩展为Printable(这可能发生在一个完全不同的模块中)。如果不创建许多奇怪的极端情况(例如,内部事物的行为与公共事物不同),并且不强制 Swift 主动生成未来调用者可能需要的每个可能的display 版本,就很难解决这个问题。 Swift 可能会及时改进,但我认为这是一个难题。 (Swift 已经遭受了一些性能下降,因此公共泛型可以在不访问原始源代码的情况下进行专门化。这会使这个问题变得更加复杂。)

    好的,所以我们明白了。 T 不是 Printable。但是,如果我们有一个类型 明确的 Printable,我们在编译时就知道并存在于这个函数中呢?那它会起作用吗?

    func display() {
        if let p = args as? Printable {
            print(Printer.print(p))
        } else {
            print(Printer.print(args))
        }
    }
    

    哦,这么近……但不完全是。这几乎有效。 if-let 实际上完全符合您的要求。 p 被分配。这是Printable。但它仍然调用非打印函数。 ?!?!?!?!

    这是一个我个人认为 Swift 刚刚被破坏并寄予厚望的地方,它会被修复。它甚至可能是一个错误。问题是Printable 本身不符合Printable。是的,我也不明白,但你去吧。所以我们需要做一些确实符合Printable的东西,以便获得正确的重载。像往常一样,type erasers 来救援。

    struct AnyPrintable: Printable {
        let value: Printable
    }
    
    struct Generic<T> {
        var args: T
    
        func display() {
            if let p = args as? Printable {
                print(Printer.print(AnyPrintable(value: p)))
            } else {
                print(Printer.print(args))
            }
        }
    }
    

    这将按照您想要的方式打印。 (假设Printable 需要一些方法,您只需将这些方法添加到AnyPrintable 类型的橡皮擦即可。)

    当然正确的答案是不要在Printer 中以这种方式使用泛型重载。它太混乱和脆弱了。它看起来很漂亮,但它一直在爆炸。

    【讨论】:

    • 非常感谢您提供这些信息。适合我现在正在尝试做的事情看起来很复杂。 github.com/RahulKatariya/Reactofire/blob/develop/… .. 我正在尝试使 GenericResponseString 和 GenericResponsePerson 测试通过。
    • 祝你好运……我用这些超级聪明/神奇的方法来解决基本问题做了很多实验。就我个人而言,我发现几行简单的代码,即使你偶尔不得不在这里或那里重复几行乏味的行,效果要好得多,尤其是在调试的时候(我现在只是guard-let我的 JSON 解析;这么多更轻松)。这并不令人兴奋,但在我的项目中效果要好得多。但祝那些仍在探索前沿的人好运!这很有趣,但有很多像这样的挫败感。
    【解决方案3】:

    在我看来,您唯一的选择是在“print()”函数中使用 if-else 和类型转换

    static func print<T>(object: T) -> String {
        if let _ = object as? Printable {
            return "Printable"
        }
        return "T"
    }
    

    或非通用变体

    static func print(object: Any) -> String {
        if let _ = object as? Printable {
            return "Printable"
        }
        return "T"
    }
    

    【讨论】:

    • 谢谢。但是该代码来自另一个来源,我无法修改它。我只能在 Generic 中进行更改。
    猜你喜欢
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 2010-09-16
    • 1970-01-01
    • 2016-03-21
    • 2014-10-22
    • 2021-07-18
    相关资源
    最近更新 更多