Swift Keypath 泛型和子类化
Swift Keypath Generic and Subclassing
我正在研究 class,其中 superclass 及其 subclass 具有不同的属性,但所有类型都相同,在分配之前需要相同的处理.我想出了一个非常人为和简化的例子,说明我试图用关键路径做什么,可能使用泛型,包括一个非最佳但有效的变体。
class OriginalClass {
// this will only allow for properties that exist available on the base `OriginalClass` (and that makes sense)
func updateAProperty(to value: Int, keyPath: ReferenceWritableKeyPath<OriginalClass, Int>) {
// lots of custom, but common logic
self[keyPath: keyPath] = value
}
// this *works*, but I don't like the cast I have to do on the first line, and the call site requires explicit
// keypaths (including the type)
func updateAPropertyTwo<GenClass>(to value: Int, keyPath: ReferenceWritableKeyPath<GenClass, Int>) {
guard let self = self as? GenClass else { return }
self[keyPath: keyPath] = value
}
// ideally, i want to do something like this. Basically, the compiler should (aka i WANT it to) be able to tell that
// im working off a subclass of OriginalClass and provide the keypaths available to the subclass in addition to those
// on the base, superclass.
// func idealNonworking(to value: Int, keyPath: ReferenceWritableKeyPath<*AutomaticallyReplacedWithWhateverSubclass*, Int>) {
// // lots of custom, but common logic
// self[keyPath: keyPath] = value
// }
// this complains that `Same-type requirement makes generic parameter 'GenClass' non-generic`, but afaik, if it DID
// work it SHOULD include the subclass properties (but it doesn't, to be clear)
// func alternativeIdealYetNonworking<GenClass>(to value: Int, keyPath: ReferenceWritableKeyPath<GenClass, Int>) where GenClass == Self {
// // lots of custom, but common logic
// self[keyPath: keyPath] = value
// }
}
class SecondClass: OriginalClass {
var subclassValue = 0
func nonWorkingExample() {
// updateAProperty(to: subclassValue + 1, keyPath: \.subclassValue)
}
func subOptimalWorkingExample() {
updateAPropertyTwo(to: subclassValue + 1, keyPath: \SecondClass.subclassValue)
// updateAPropertyTwo(to: subclassValue + 1, keyPath: \Self.subclassValue) // runs into a runtime demangling error
// updateAPropertyTwo(to: subclassValue + 1, keyPath: \.subclassValue)
}
// func optimalYetNonworkingExample() {
// idealNonworking(to subclassValue + 1, keyPath: \.subclassValue)
// }
}
let test = SecondClass()
print(test.subclassValue)
test.subOptimalWorkingExample()
print(test.subclassValue)
现在,我知道为什么第一个不起作用(关键路径类型由 OriginalClass
上可用的属性定义),但我不确定为什么最后一个不起作用.当然,这不如有人知道怎么做重要
Self
是协议扩展允许的,所以我只写了:
protocol P {
// put whatever methods and properties from OriginalClass the "lots of custom,
// but common logic" need here...
}
class OriginalClass : P {}
extension P {
func updateAProperty(to value: Int, keyPath: ReferenceWritableKeyPath<Self, Int>) {
// lots of custom, but common logic
self[keyPath: keyPath] = value
}
}
和用法:
class SecondClass: OriginalClass {
var subclassValue = 0
func workingExample() {
updateAProperty(to: subclassValue + 1, keyPath: \.subclassValue)
}
}
let test = SecondClass()
print(test.subclassValue)
test.workingExample()
print(test.subclassValue)
我正在研究 class,其中 superclass 及其 subclass 具有不同的属性,但所有类型都相同,在分配之前需要相同的处理.我想出了一个非常人为和简化的例子,说明我试图用关键路径做什么,可能使用泛型,包括一个非最佳但有效的变体。
class OriginalClass {
// this will only allow for properties that exist available on the base `OriginalClass` (and that makes sense)
func updateAProperty(to value: Int, keyPath: ReferenceWritableKeyPath<OriginalClass, Int>) {
// lots of custom, but common logic
self[keyPath: keyPath] = value
}
// this *works*, but I don't like the cast I have to do on the first line, and the call site requires explicit
// keypaths (including the type)
func updateAPropertyTwo<GenClass>(to value: Int, keyPath: ReferenceWritableKeyPath<GenClass, Int>) {
guard let self = self as? GenClass else { return }
self[keyPath: keyPath] = value
}
// ideally, i want to do something like this. Basically, the compiler should (aka i WANT it to) be able to tell that
// im working off a subclass of OriginalClass and provide the keypaths available to the subclass in addition to those
// on the base, superclass.
// func idealNonworking(to value: Int, keyPath: ReferenceWritableKeyPath<*AutomaticallyReplacedWithWhateverSubclass*, Int>) {
// // lots of custom, but common logic
// self[keyPath: keyPath] = value
// }
// this complains that `Same-type requirement makes generic parameter 'GenClass' non-generic`, but afaik, if it DID
// work it SHOULD include the subclass properties (but it doesn't, to be clear)
// func alternativeIdealYetNonworking<GenClass>(to value: Int, keyPath: ReferenceWritableKeyPath<GenClass, Int>) where GenClass == Self {
// // lots of custom, but common logic
// self[keyPath: keyPath] = value
// }
}
class SecondClass: OriginalClass {
var subclassValue = 0
func nonWorkingExample() {
// updateAProperty(to: subclassValue + 1, keyPath: \.subclassValue)
}
func subOptimalWorkingExample() {
updateAPropertyTwo(to: subclassValue + 1, keyPath: \SecondClass.subclassValue)
// updateAPropertyTwo(to: subclassValue + 1, keyPath: \Self.subclassValue) // runs into a runtime demangling error
// updateAPropertyTwo(to: subclassValue + 1, keyPath: \.subclassValue)
}
// func optimalYetNonworkingExample() {
// idealNonworking(to subclassValue + 1, keyPath: \.subclassValue)
// }
}
let test = SecondClass()
print(test.subclassValue)
test.subOptimalWorkingExample()
print(test.subclassValue)
现在,我知道为什么第一个不起作用(关键路径类型由 OriginalClass
上可用的属性定义),但我不确定为什么最后一个不起作用.当然,这不如有人知道怎么做重要
Self
是协议扩展允许的,所以我只写了:
protocol P {
// put whatever methods and properties from OriginalClass the "lots of custom,
// but common logic" need here...
}
class OriginalClass : P {}
extension P {
func updateAProperty(to value: Int, keyPath: ReferenceWritableKeyPath<Self, Int>) {
// lots of custom, but common logic
self[keyPath: keyPath] = value
}
}
和用法:
class SecondClass: OriginalClass {
var subclassValue = 0
func workingExample() {
updateAProperty(to: subclassValue + 1, keyPath: \.subclassValue)
}
}
let test = SecondClass()
print(test.subclassValue)
test.workingExample()
print(test.subclassValue)