使用Swift语言本身没有办法模仿Codable,因为Codable的实现依赖于编译器生成特殊代码。具体来说,没有协议扩展可以创建默认的CodingKeys枚举,编译器会自动在符合Codable的类型中创建该枚举,除非您自己指定。
这类似于Swift编译器将自动为结构体创建初始化程序(“成员初始化程序”),除非您指定自己的初始化程序。在这种情况下,也没有协议扩展或Swift语言功能可用于复制自动生成的结构体初始化程序,因为它基于元编程/代码生成,即由编译器生成。
有一些工具,例如Sourcery(https://github.com/krzysztofzablocki/Sourcery),允许您实现自己的元编程和代码生成。使用Sourcery,您可以在构建阶段运行脚本,自动生成所需的Command
枚举代码,并将其添加到符合CommandHandler
的任何类型中。
这本质上模仿了 Codable 如何通过 Swift 编译器生成所需代码的方式。但在这两种情况下,都不是通过像协议扩展等 Swift 语言特性来实现的。相反,这是一个脚本编写的样板源代码,而不必手动编写。
更新修订后的问题
如果你只需要确保有一种方法可以枚举所有的CommandIds枚举类型,那么你可以像这样向CommandId协议添加一个协议要求:
protocol CommandId {
static var all: [Self] { get }
}
那么实现就需要像这样:
class HandlerA : CommandHandler {
enum CommandIds : String, CommandId {
case commandA1
case commandA2
static var all: [CommandIds] { return [.commandA1, .commandA2] }
}
}
而你的处理函数可能看起来像:
func processHandler<T:CommandHandler>(_ handler:T){
T.CommandIds.all.forEach {
}
值得继续注意的是,对于 Codable,Swift 并没有或使用任何语言功能来枚举所有情况。相反,编译器利用符合 Codable 的类型的所有属性知识为该类型生成特定的 init(from decoder: Decoder)
实现,包括基于已知的属性名称和类型的每个情况的行,例如:
struct Example: Codable {
let name: String
let number: Int
}
extension Example {
enum CodingKeys: String, CodingKey {
case name, number
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
number = try values.decode(Int.self, forKey: .number)
}
}
由于Swift是一种大多数静态语言,具有极其有限的运行时反射能力(目前),因此无法使用语言特性在运行时执行这些类型的任务。
但是,没有任何阻止您或任何开发人员使用代码生成方式来完成类似的便利,就像Swift编译器一样。实际上,苹果Swift核心团队的知名成员甚至在我向他展示我在WWDC中面临的一些挑战时鼓励我这样做。
值得注意的是,现在已成为Swift编译器一部分或具有打开拉取请求以添加到Swift编译器中的功能(例如Codable和自动符合Equatable和Hashable)是在使用Sourcery创建和实现的真实Swift项目中首先被创建和实现的。