我如何键入检查泛型然后快速将其强制转换?

时间:2019-06-01 17:13:50

标签: swift generics casting

说我有一种方法,例如:

extension NSManagedObjectContext: Database {

    func loadObjects<T: Model>(matching query: Query<T>) -> [T] {

        // you need a fetch request for these models.  This guard statement compiles.  How do we make it work with NSFetchRequestResult however?
        guard T.self is NSManagedObject.Type else {
            return []
        }

        // This line below Fails compiling.  Type 'T' does not conform to protocol 'NSFetchRequestResult'
        var request = NSFetchRequest<T>(entityName: String(describing: T))


        var objects: [T] = []
        self.performAndWait {
            do {
                if let results = try self.fetch(request!) as? [T] {
                    objects = results
                }

            } catch let error {
                print("Error fetching: \(error.localizedDescription)")
            }
        }
        return objects
    }
}

因此,如果我可以确定T的类型是否是一种NSManagedObject,那么是否可以通过将T强制转换为可以工作的对象来实例化NSFetchRequest?

数据库是一种与技术无关的协议,因此不应对Core Data一无所知。如果需要更改数据持久性框架,我想这样做。

typealias Database = ReadableDatabase & WritableDatabase

protocol ReadableDatabase {
    func loadObjects<T: Model>(matching query: Query<T>) -> [T]
    func loadObject<T: Model>(withID identifier: String) -> T?
}

protocol WritableDatabase {
    func save<T: Model>(_ object: T)
}

2 个答案:

答案 0 :(得分:0)

例如,不是在运行时检查类型,而是在编译时约束类型。

extension NSManagedObjectContext: Database {

    func loadObjects<T: NSManagedObject>(matching query: Query<T>) -> [T] {

        let request = NSFetchRequest<T>(entityName: String(describing: T.self))

        var objects = [T]()
        self.performAndWait {
            do {
                objects = try self.fetch(request) // a generic fetch request returns an array of the generic type or throws an error
            } catch let error {
                print("Error fetching: \(error.localizedDescription)")
            }
        }
        return objects
    }
}

答案 1 :(得分:0)

看来我可以回答自己的问题了。可以通过重载泛型来进一步限制泛型,以便调用代码可以保持不变,但是调用的方式取决于传递给它的类型。

下面的代码在操场上进行了演示:

public protocol Action {
    func doSomething<T>(to object: T)
}

public class MyActor {

}

extension MyActor: Action {

    // works for any type
    public func doSomething<T>(to object: T) {
        print("was generic")
    }

    // but if you constrain the type and your object fits that constraint...
    // this code is called (same method signature)
    public func doSomething<T: NSObject>(to object: T) {
        print("was an object")
    }
}

class MyObject: NSObject {
    var name: String = "Object"
}

struct MyStruct {
    var name: String = "Struct"
}

let actor = MyActor()
let object = MyObject()
let value = MyStruct()

actor.doSomething(to: value) // prints 'was generic'
actor.doSomething(to: object)  // prints 'was an object'

因此,在原始示例中,我将通过以下方式为CoreData支持Database

extension NSManagedObjectContext: Database {

    func loadObjects<T>(matching query: Query<T>) -> [T] {
       return [] // return an empty array because we only support NSManagedObject types
    }

    func loadObjects<T: NSManagedObject>(matching query: Query<T>) -> [T] {

        let request = NSFetchRequest<T>(entityName: String(describing: T.self))

        var objects = [T]()
        self.performAndWait {
            do {
                objects = try self.fetch(request) // a generic fetch request returns an array of the generic type or throws an error
            } catch let error {
                print("Error fetching: \(error.localizedDescription)")
            }
        }
        return objects
    }
}