代码之家  ›  专栏  ›  技术社区  ›  Dev Khadka

Swift:将未受约束的泛型类型强制转换为确认为可解码的泛型类型

  •  0
  • Dev Khadka  · 技术社区  · 8 年前

    情况

    • 我有两个通用类,它们将从api和数据库中获取数据,比如说APIDataSource<一、 O>和DBDataSource<一、 O>分别地

    • 在创建视图模型时,我将在视图模型中注入两个类中的任何一个,视图模型将使用该类获取所需的数据。我希望视图模型对这两个类的工作方式完全相同。所以我不希望类有不同的泛型约束

      //sudo代码

      ViewModel(APIDataSource<InputModel,ResponseModel>(…))

      //我想在将来更改数据源,比如

      ViewModel(DBDataSource<InputModel,ResponseModel>(…))

    • 要从api ResponseModel获取数据,需要确认为“可解码”,因为我想从JSON创建该对象。要从领域数据库获取数据,它需要从对象继承

    • Inside ViewModel我想得到这样的回应

      //sudo代码

      自己数据源。请求(“参数1”、“参数2”)

    • 若开发人员试图从数据库获取api数据,反之亦然,它将检查正确的类型并抛出正确的错误。

    操场的精简版代码

    下面是代码的精简版本,显示了我想要实现的目标或我的困境(将未受约束的泛型类型转换为确认为可解码的泛型类型)

    import Foundation 
    // Just to test functions below
    class DummyModel: Decodable {
    
    }
    
    // Stripped out version of function which will convert json to object of type T
    func decode<T:Decodable>(_ type: T.Type){
        print(type)
    }
    
    // This doesn't give compilation error
    // Ignore the inp
    func testDecode<T:Decodable> (_ inp: T) {
        decode(T.self)
    }
    
    
    // This gives compilation error
    // Ignore the inp
    func testDecode2<T>(_ inp: T){
        if(T.self is Decodable){
            // ??????????
            // How can we cast T at runtime after checking T confirms to Decodable??
            decode(T.self as! Decodable.Type)
        }
    }
    
    
    
    testDecode(DummyModel())
    

    如果您能提供任何帮助或解释,我们将不胜感激。提前感谢:)

    2 回复  |  直到 8 年前
        1
  •  2
  •   Rob Napier    8 年前

    正如@matt所建议的,将我的各种评论转移到“你的问题没有好的解决方案,你需要重新设计你的问题”的形式上

    你想做的事情充其量是脆弱的,最坏的情况下是不可能的。马特的方法是一个很好的解决方案,当你试图提高性能,但它打破了令人惊讶的方式,如果它影响行为。例如:

    protocol P {}
    
    func doSomething<T>(x: T) -> String {
        if x is P {
            return "\(x) simple, but it's really P"
        }
        return "\(x) simple"
    }
    
    func doSomething<T: P>(x: T) -> String {
        return "\(x) is P"
    }
    
    struct S: P {}
    
    doSomething(x: S())   // S() is P
    

    所以这就像我们预期的那样。但我们可以通过这种方式丢失类型信息:

    func wrapper<T>(x: T) -> String {
        return doSomething(x: x)
    }
    
    wrapper(x: S())  // S() simple, but it's really P!
    

    所以你不能用泛型来解决这个问题。

    回到你的方法,它至少有可能变得强大,但它仍然不起作用。斯威夫特的打字系统无法表达你想说的话。但我认为你无论如何都不应该这么说。

    在获取数据的方法中,我将检查泛型类型的类型,如果它符合“可解码”协议,我将使用它从数据库的api else中获取数据。

    如果从API抓取与从数据库抓取表示不同的语义(而不仅仅是性能改进),那么即使可以让它工作,这也是非常危险的。程序的任何部分都可以附加 Decodable 任何类型的。它甚至可以在单独的模块中完成。添加协议一致性永远不会改变程序的语义(外观可见的行为),只会改变性能或功能。

    我有一个泛型类,可以从api或数据库中获取数据

    完美的如果你已经有了一个类,那么类继承在这里很有意义。我可能会这样做:

    class Model {
        required init(identifier: String) {}
    }
    
    class DatabaseModel {
        required init(fromDatabaseWithIdentifier: String) {}
        convenience init(identifier: String) { self.init(fromDatabaseWithIdentifier: identifier )}
    }
    
    class APIModel {
        required init(fromAPIWithIdentifier: String) {}
        convenience init(identifier: String) { self.init(fromAPIWithIdentifier: identifier )}
    }
    
    class SomeModel: DatabaseModel {
        required init(fromDatabaseWithIdentifier identifier: String) {
            super.init(fromDatabaseWithIdentifier: identifier)
        }
    }
    

    根据您的具体需求,您可能会重新安排(在这里,协议也可能是可行的)。但关键是 模型 知道如何找回自己。这使得在类内部使用Decodable变得很容易(因为它可以很容易地使用 type(of: self) 作为参数)。

    你的需求可能不同,如果你能更好地描述它们,也许我们会找到更好的解决方案。但它不应该基于某件事是否仅仅符合协议。在大多数情况下,这是不可能的,如果你让它工作,它将是脆弱的。

        2
  •  1
  •   matt    8 年前

    你真正想做的是有两个版本的 testDecode ,一个表示当T符合可解码时,另一个表示当T不符合可解码时。因此 超载 功能 测试解码 因此,根据T的类型调用正确的一个。

    不幸的是,您不能这样做,因为您不能执行依赖于泛型类型解析的函数重载。但可以通过将函数装箱到泛型中来解决这个问题 类型 ,因为可以有条件地扩展该类型。

    因此,为了展示架构:

    protocol P{}
    struct Box<T> {
        func f() {
            print("it doesn't conform to P")
        }
    }
    extension Box where T : P {
        func f() {
            print("it conforms to P")
        }
    }
    
    struct S1:P {}
    struct S2 {}
    let b1 = Box<S1>()
    b1.f() // "it conforms to P"
    let b2 = Box<S2>()
    b2.f() // "it doesn't conform to P"
    

    这证明了 f 正在调用,这取决于解析泛型的类型是否符合协议。