Swift-密钥路径扩展-强制遵守协议
理想情况下,我希望获得键路径引用的属性的名称。但在Swift中,这似乎不可能开箱即用 所以我的想法是,KeyPath可以根据开发人员添加的协议扩展提供这些信息。然后,我想设计一个带有初始值设定项/函数的API,它接受符合该协议的密钥路径(添加计算属性) 到目前为止,我只能定义协议和协议的条件一致性。下面的代码编译得很好Swift-密钥路径扩展-强制遵守协议,swift,Swift,理想情况下,我希望获得键路径引用的属性的名称。但在Swift中,这似乎不可能开箱即用 所以我的想法是,KeyPath可以根据开发人员添加的协议扩展提供这些信息。然后,我想设计一个带有初始值设定项/函数的API,它接受符合该协议的密钥路径(添加计算属性) 到目前为止,我只能定义协议和协议的条件一致性。下面的代码编译得很好 protocol KeyPathPropertyNameProviding { var propertyName: String {get} } struct User
protocol KeyPathPropertyNameProviding {
var propertyName: String {get}
}
struct User {
var name: String
var age: Int
}
struct Person {
var name: String
var age: Int
}
extension KeyPath: KeyPathPropertyNameProviding where Root == Person {
var propertyName: String {
switch self {
case \Person.name: return "name"
case \Person.age: return "age"
default: return ""
}
}
}
struct PropertyWrapper<Model> {
var propertyName: String = ""
init<T>(property: KeyPath<Model, T>) {
if let property = property as? KeyPathPropertyNameProviding {
self.propertyName = property.propertyName
}
}
}
let userAge = \User.age as? KeyPathPropertyNameProviding
print(userAge?.propertyName) // "nil"
let personAge = \Person.age as? KeyPathPropertyNameProviding
print(personAge?.propertyName) // "age"
let wrapper = PropertyWrapper<Person>(property: \.age)
print(wrapper.propertyName) // "age"
任何提示都非常感谢 您误解了条件一致性。您似乎希望在将来这样做:
extension KeyPath: KeyPathPropertyNameProviding where Root == Person {
var propertyName: String {
switch self {
case \Person.name: return "name"
case \Person.age: return "age"
default: return ""
}
}
}
extension KeyPath: KeyPathPropertyNameProviding where Root == User {
var propertyName: String {
...
}
}
extension KeyPath: KeyPathPropertyNameProviding where Root == AnotherType {
var propertyName: String {
...
}
}
但你不能。您试图指定多个条件以符合同一协议。有关为什么Swift中没有此选项的更多信息,请参阅
不知何故,编译器的一部分认为与KeyPath-propertyNameProvising
的一致性不是有条件的,因此KeyPath&KeyPath-propertyNameProvising
实际上与KeyPath
相同,因为KeyPath
已经“一致”对于KeyPathPropertyName
,就编译器而言,只是属性propertyName
有时才可用
如果我这样重写初始化器
init<T, KeyPathType: KeyPath<Model, T> & KeyPathPropertyNameProviding>(property: KeyPathType) {
self.propertyName = property.propertyName
}
init(属性:KeyPathType){
self.propertyName=property.propertyName
}
这会以某种方式使错误消失并产生警告:
冗余一致性约束“KeyPathType”:“KeyPathPropertyNameProvising”
密钥路径是可散列的,因此我建议使用字典。如果您能够使用
CodingKey
类型,那么将其与强类型组合起来就特别容易
struct Person:Codable{
变量名称:String
变量年龄:Int
枚举编码键:Swift.CodingKey{
案例名称
病例年龄
}
}
扩展PartialKeyPath,其中Root==个人{
变量标签:字符串{
[\Root.name:Root.CodingKey.name,
\Root.age:.age
].mapValues(\.stringValue)[self]!
}
}
然后使用括号,而不是您演示的类型转换。目前还不需要协议
(\Person.name).label/“name”
(\Person.age).标签//“age”
有一天,由于内置的支持,这一切可能会更干净
init<T, KeyPathType: KeyPath<Model, T> & KeyPathPropertyNameProviding>(property: KeyPathType) {
self.propertyName = property.propertyName
}