You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift4中使用泛型T.Type类时如何调用正确的构造器?

打造通用的数据库泛型读取函数

嘿,我懂你想做的事——不用为每个数据库表写重复的读取逻辑,一个泛型函数搞定所有对象的SQL映射,这个思路超棒!咱们来一步步把你的想法落地,解决调用时只能用特定对象的问题。

第一步:先定义一个映射协议

要让泛型函数能把SQL结果转成任意对象,首先得给所有对象定个“规矩”:它们必须知道怎么从SQL查询结果里创建自己。咱们定义一个协议:

protocol SQLMappable {
    init(from sqlResult: [String: Any]) throws
}

这个协议要求每个遵守它的类,都实现一个从SQL字典(键是字段名,值是字段值)初始化自己的方法,这样泛型函数就能统一调用这个方法来创建对象了。

第二步:重构你的泛型函数

接下来修改你的readAll函数,给泛型T加上协议约束,这样函数里就能安全地调用初始化方法。同时,咱们把SQL查询的逻辑补全,还要处理可能的错误(比如查询失败、初始化失败):

func readAll<T: SQLMappable>(tableName: String, typeClass: T.Type) throws -> [T] {
    var results: [T] = []
    
    // 1. 执行SQL查询,这里替换成你实际的SQL执行逻辑
    let sqlQuery = "SELECT * FROM \(tableName)"
    guard let sqlResults = executeSQL(sqlQuery) else {
        throw DatabaseError.queryFailed
    }
    
    // 2. 把每条SQL结果转成T对象
    for row in sqlResults {
        do {
            let object = try typeClass.init(from: row)
            results.append(object)
        } catch {
            throw DatabaseError.failedToMapObject(error)
        }
    }
    
    return results
}

// 辅助的错误定义,方便处理异常
enum DatabaseError: Error {
    case queryFailed
    case failedToMapObject(Error)
}

// 你实际的SQL执行函数,这里只是示例
func executeSQL(_ query: String) -> [[String: Any]]? {
    // 这里写你的数据库查询逻辑,返回查询到的行,每行是字段名到值的字典
    return []
}

第三步:让你的对象类遵守协议

比如你有个User类对应users表,就让它实现SQLMappable协议:

class User: SQLMappable {
    let id: Int
    let name: String
    let email: String
    
    init(id: Int, name: String, email: String) {
        self.id = id
        self.name = name
        self.email = email
    }
    
    // 实现协议要求的初始化方法
    required init(from sqlResult: [String: Any]) throws {
        guard let id = sqlResult["id"] as? Int,
              let name = sqlResult["name"] as? String,
              let email = sqlResult["email"] as? String else {
            throw MappingError.missingRequiredFields
        }
        self.id = id
        self.name = name
        self.email = email
    }
}

enum MappingError: Error {
    case missingRequiredFields
}

第四步:正确调用泛型函数

现在你就可以用任意遵守SQLMappable的类型来调用这个函数了,不用再依赖特定对象:

do {
    // 读取users表,转成User数组
    let users = try readAll(tableName: "users", typeClass: User.self)
    
    // 读取products表,转成Product数组(假设你有Product类且遵守协议)
    let products = try readAll(tableName: "products", typeClass: Product.self)
} catch {
    print("读取数据失败:\(error)")
}

这样就实现了你想要的真正泛型效果——同一个函数可以处理任何和数据库表对应的对象,只要对象遵守了映射协议就行。

内容的提问来源于stack exchange,提问作者Augusto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:15:33