使用Swift推断类方法中的泛型类型 [英] Infer Generic Type in Class Method with Swift

查看:140
本文介绍了使用Swift推断类方法中的泛型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

泛型方法是否有可能根据正在执行的类来推断它的类型?我使用CoreData NSManagedObject模型来存储和检索本地数据,并且设法使所有东西都变得通用,易于阅读和使用,除了在一个地方。如果用户希望查询本地数据库以获取对象列表,他会编写以下行:

  let posts: [Post] = Post.all()

这会正确返回数据库中的所有Post对象,但语法要求在从 Post >调用方法之前定义类型( [Post] )。类本身( Post.all()),这感觉不必要的冗余。是否有任何方法通过调用 Post 类中的 all()方法来定义泛型类型?我想我可以创建全局函数来获取数据,如下所示:

  let posts:[Post] = all()

如果语法如下所示,这并不像可读性那样可读:

  let posts = Post.all()

试图改进这一点的目的是让所有选择此项目的开发人员能够在不费力气的情况下快速学习结构和风格。此外,这将有望提高将来的通用代码可读性,无论是否有人正在使用它或出于某种其他原因只是阅读它。



有关更多信息,请点击此处是关于当前结构的更多信息:

  // Model.swift  - 模型基类。所有模型都扩展了这个类。 

class Model:NSManagedObject {
/ **
这里有些其他的东西
** /

// MARK:Fetch
internal class func fetch< T:Model>(predicate:NSPredicate?= nil) - > [T]? {
do {
if let request = NSFetchRequest.FromEntityName(self.entityName){//获取当前类中定义的名称的实体
request.predicate =谓词
if让结果=尝试self.context?.executeFetchRequest(请求)为? [T] {
返回结果
}
}
}
将错误记录为NSError {
Log.Error(\(error) )
}
return nil
}

// MARK:取得一般
class func all< T:Model>() - > [T]? {
if let result:[T] = self.fetch(){
return result
}
Log.warning(No \(self.entityName)found)
返回零
}
}

//Post.swift - 示例模型类。扩展Model.swift

类Post:Model {
//一些字段
}

//示例视图控制器
类ViewController: UIViewController {
覆盖func viewDidLoad(){
let posts:[Post] = Post.all()
// do stuff
}
}

如果有人对此有所了解,请告诉我。所有帮助表示赞赏!

解决方案

在一般情况下,类方法返回类的类型即使对于子类,也是使用协议扩展和 Self 类型。这里有一个例子,将你的方法简化到最低限度,以便按照你想要的方式工作:

  // define一个协议
协议ModelType {}
//在协议上创建一个静态方法返回[Self]
extension ModelType其中Self:NSManagedObject {
static func all() - > ; [自]? {
return [Self]()//在此执行
}
}
//符合您的类层次结构中的协议
class Model:NSManagedObject, ModelType {}
class Post:Model {}

posts = Post.all()
// posts'的隐式类型是`[Post]?`

请注意, all()应由协议扩展,但不是协议的要求。如果你在 protocol ModelType 中声明 all(),那么你不能使用动态分派,这是必要的如果是使用动态类型的话。






另外,请注意在Swift 3(和macOS 10.12 / iOS 10 / tvOS 10 / watchOS 3),核心数据本身定义了一些Swift API快捷方式,它们替代了您为自己定义的一些快捷方式。请注意,此示例来自什么是核心数据新增功能

  func findAnimals(){
context.performAndWait({
let request = Animal.fetchRequest //隐式地NSFetchRequest< Animal>
do {
let searchResults = try request.execute()
//使用searchResults ...
} catch {
print(Error with request:\(error))
}
})
}

$ hr

最后,对你选择的风格进行一些评论......

< blockquote>

fyi我将所有静态/类方法中的第一个字母大写为约定


试图改进这一点的目的是让任何接受此操作的开发人员项目可以毫不费力地快速学习结构和风格。此外,这将有望提高将来的通用代码可读性。


我不确定是否违反语言标准惯例(如在 Swift 3 API指南中推荐的小写方法名称)与您的制作目标非常相容对于其他新开发者来说,读取和参与代码库很容易。


Is it possible for a generic method to infer its type based on the class in which it is being executed? I use CoreData NSManagedObject models to store and retrieve local data, and have managed to make everything generic in an easy to read and usable way, except for in one place. If a user wishes to query the local database to fetch a list of objects, he would write the following line:

let posts: [Post] = Post.all()

This will properly return "all" Post objects in the database, but the syntax requires that the type be defined ([Post]) on top of calling the method from the Post class itself (Post.all()), which feels unnecessarily redundant. Is there any way to define the generic type simply by calling the all() method from the Post class? I imagine I could just create global functions for fetching data, like so:

let posts: [Post] = all()

This doesn't feel nearly as readable as it would be if the syntax was as follows:

let posts = Post.all()

The point of trying to improve this is so that any developers who pick up this project can quickly learn the structure and style without much effort. Also, this will hopefully increase general code readability in the future, regardless of if someone is working on it or just reading it for some other reason.

For more insight, here is a bit more information about the current structure:

//Model.swift - The model base class. All models extend this class.

class Model: NSManagedObject {
    /**
    Some other stuff here
    **/

    //MARK: Fetch
    internal class func fetch<T: Model>(predicate: NSPredicate? = nil) -> [T]? {
        do {
            if let request = NSFetchRequest.FromEntityName(self.entityName) { //Get entity with the name defined in the current class
                request.predicate = predicate
                if let result = try self.context?.executeFetchRequest(request) as? [T] {
                    return result
                }
            }
        }
        catch let error as NSError {
            Log.Error("\(error)")
        }
        return nil
    }

    //MARK: Fetch general
    class func all<T: Model>() -> [T]? {
        if let result: [T] = self.fetch() {
            return result
        }
        Log.warning("No \(self.entityName) found")
        return nil
    }
}

//Post.swift - An example model class. Extends Model.swift

class Post: Model {
    //some fields
}

//Example view controller
class ViewController: UIViewController {
    override func viewDidLoad() {
        let posts: [Post] = Post.all()
        //do stuff
    }
}

If anyone has an idea about then please let me know. All help is appreciated!

解决方案

In the general case, the typical way for a class method to return "type of the class" even for subclasses is to use protocol extensions and the Self type. Here's an example that boils your approach down to the bare minimum to make the type checking work the way you want:

// define a protocol
protocol ModelType {}
// create a static method on the protocol that returns [Self]
extension ModelType where Self: NSManagedObject {
    static func all() -> [Self]? {
        return [Self]() // do your fetch here
    }
}
// conform to the protocol in your class hierarchy
class Model: NSManagedObject, ModelType {}
class Post: Model {}

let posts = Post.all()
// implicit type of `posts` is `[Post]?`

Note that all() should be provided by the protocol extension, but not a requirement of the protocol. If you declare all() inside protocol ModelType, then you can't make it use dynamic dispatch, which is necessary if it's to use a dynamic type.


Also, note that in Swift 3 (and macOS 10.12 / iOS 10 / tvOS 10 / watchOS 3), Core Data itself defines some Swift API shortcuts that replace some of the ones you've defined for yourself. Note this example from What's New in Core Data:

func findAnimals() {
    context.performAndWait({
        let request = Animal.fetchRequest // implicitly NSFetchRequest<Animal>
        do {
            let searchResults = try request.execute()
            // use searchResults ...
        } catch {
            print("Error with request: \(error)")
        }
    })
}


Finally, some commentary on your choice of style...

fyi I capitalize the first letter in all static/class methods just as a convention

The point of trying to improve this is so that any developers who pick up this project can quickly learn the structure and style without much effort. Also, this will hopefully increase general code readability in the future

I'm not sure that breaking from language-standard conventions (like the lowercase method names recommended in the Swift 3 API Guidelines) is very compatible with your goal of making it easy for other developers new to your codebase to read and participate.

这篇关于使用Swift推断类方法中的泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆