【问题标题】:Using the Correct Type-Constrained Variants with Swift Generics在 Swift 泛型中使用正确的类型约束变体
【发布时间】:2021-05-09 23:23:17
【问题描述】:

给定一个具有两个泛型和多个类型约束初始化器的类型:

struct ComposableRequest <T, U> {

    init() {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    var t: T?
    var u: U?

    private func defaultT() -> T? {
        nil
    }
    
    private func defaultT() -> T? where T == String {
        "Test1"
    }
    
    private func defaultT() -> T? where T == Int {
        1
    }
    
    private func defaultU() -> U? {
        nil
    }
    
    private func defaultU() -> U? where U == String {
        "Test2"
    }
    
    private func defaultU() -> U? where U == Int {
        2
    }
}

我似乎永远无法获得默认电镀方法的类型约束版本:

let first = ComposableRequest<String, Int>()
print(first.t) // I'd expect this to be `"Test1"`, but its nil!
print(first.u) // I'd expect this to be `2`, but its nil!

当满足多个类型约束选项时,Swift 如何决定使用哪些类型约束方法?如果选择的方法不明确,这段代码是否应该编译?如何调用更专业的版本?

【问题讨论】:

    标签: ios swift generics type-constraints


    【解决方案1】:

    此时的问题是,您正在做的事情几乎没有任何通用性。正是由于这个原因,您可以轻松获得您想要的结果,而无需尝试使用where 子句作为一种假重载:

    struct MyGeneric<T, U> {
    
        init() {
            self.t = self.defaultT()
            self.u = self.defaultU()
        }
        
        var t: T?
        var u: U?
    
        private func defaultT() -> T? {
            if T.self is String.Type {
                return Optional("Test" as! T)
            }
            if T.self is Int.Type {
                return Optional(1 as! T)
            }
            return nil
        }
        
        private func defaultU() -> U? {
            if U.self is String.Type {
                return Optional("Test2" as! U)
            }
            if U.self is Int.Type {
                return Optional(2 as! U)
            }
            return nil
        }
    }
    

    【讨论】:

    • 你可以把Optional("Test" as! T)等改成"Test" as? T,不是吗?
    • @Sweeper 是的!但这仅仅是因为在这个特定的例子中我们输出了一个 Optional。如果我们不这样做,那么有人将不得不在一段时间内强制施放某些东西。 :) 基本上我就是这样写的,以防止强制转换产生警告。
    【解决方案2】:

    重载是在编译时静态选择的。再加上 struct 方法是静态分派的,这意味着当 编译器 看到时,已经决定调用哪个 defaultT/defaultU

    self.t = defaultT()
    self.u = defaultU()
    

    当这些行运行时不会。从编译器的角度来看,TU 可以是任何类型,所以这里最好选择返回 nil 的方法。

    调用其他方法的一种方法是约束初始化器:

    init() where T == String, U == Int {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    init() where T == Int, U == String {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    init() where U == String {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    init() where U == Int {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    init() where T == String {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    init() where T == Int {
        self.t = defaultT()
        self.u = defaultU()
    }
    
    init() {
        self.t = defaultT()
        self.u = defaultU()
    }
    

    但这是很多重复...每次您想要添加对除了StringInt 之外的新类型的支持时,您需要添加更多的初始化程序...

    一种更好的方法是将defaultX 方法移动到一个名为HasDefault 的协议中:

    protocol HasDefaults {
        static func defaults() -> Self
    }
    
    extension String: HasDefaults {
        static func defaults() -> [String] {
            ["Test1", "Test2"]
        }
        
    }
    
    extension Int: HasDefaults {
        static func defaults() -> [Int] {
            [1, 2]
        }
    }
    
    struct MyGeneric<T, U> {
        init() {
            t = nil
            u = nil
        }
        
        init() where T: HasDefaults {
            t = T.defaults()[0]
            u = nil
        }
        
        init() where U: HasDefaults {
            t = nil
            u = U.defaults()[1]
        }
        
        init() where T: HasDefaults, U: HasDefaults {
            t = T.defaults()[0]
            u = U.defaults()[1]
        }
        
        var t: T?
        var u: U?
    }
    

    您甚至可以选择不允许将任何非HasDefaults 类型用作TU。这样你只需要一个初始化器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-21
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多