动态访问 属性 of 属性 of struct
Dynamically access property of property of struct
我有大量的结构,它们都有 responseId: String
并且有一个 属性 与包含 contactId: String
的 responseId
的值同名.
responseId
始终是非可选字符串。
内部对象的 contactId
也是非可选的 String
- 所有内部对象都是可选的(在运行时只有一个对象是非零的)
下面是两个例子:
protocol ContainerBase {
var responseId: String { get }
}
struct Container1: ContainerBase {
struct OtherA {
let contactId: String
let cats: [String] // Other info here, not related to OtherB
}
struct OtherB {
let contactId: String
let dogsNum: Int // Other info here, not related to OtherA
}
let responseId: String
let otherA: OtherA? // Optional
let otherB: OtherB? // Optional
}
struct Container2: ContainerBase {
struct AnotherA {
let contactId: String
let passed: Bool // Other info here, not related to AnotherB
}
struct AnotherB {
let contactId: String
let friend: String // Other info here, not related to AnotherA
}
let responseId: String
let anotherA: AnotherA? // Optional
let anotherB: AnotherB? // Optional
}
问题:
如何从 Container1
或 Container2
动态访问 contactId
? (我尝试了非动态方法,每个 ContainerN 结构都有一个额外的函数,里面有一个开关,但这变得很疯狂,因为我有太多这样的结构,我已经犯了一些错别字,忘记了一些情况,导致了错误,......我想象一下可靠的解决方案是“反射”?)。
示例:
例如,如果 Container1
的 responseId
是“otherA”,那么我应该在 属性 otherA
中寻找 contactId
。由于我有几种类型的容器,每种容器都有不同类型的不相关内部对象,因此解决方案不应特定于 Container1 或 Container2,它应该适用于任何 ContainerBase
.
我实现了一个肮脏的代码,但它会导致警告,并且在不生成警告的情况下无法正常工作。另外我认为这不能可靠地工作(这是我的 iOS 应用程序,但奇怪的是这在 linux Swift 中不起作用)。这可能吗?还是编译器故障?
let c1 = Container1(
responseId: "otherA",
otherA: Container1.OtherA(contactId: "123", cats: ["figaro"]),
otherB: nil)
c1.findContactId() // expected "123"
前面的丑陋代码:
extension ContainerBase {
func findContactId() -> String? {
let mirror = Mirror(reflecting: self)
guard let tInnerRes = mirror.children.first(where: { [=14=].label == self.responseId })?.value else { return nil }
// WARN: Conditional cast from 'Any' to 'Optional<Any>' always succeeds
guard let maybeInnerRes = (tInnerRes as? Optional<Any>) else { return nil }
guard let innerRes = maybeInnerRes else { return nil }
let innerMirror = Mirror(reflecting: innerRes)
let contactId = innerMirror.children.first(where: { [=14=].label == "contactId" })?.value as? String
return contactId
}
}
感谢任何帮助
我在 swift 论坛上问了同样的问题(但解释得更好),并从 Alexis Schultz 那里得到了 a great answer:
func findContactId() -> String? {
Mirror(reflecting: self)
.children
.first(where: { [=10=].label == responseId })
.map(\.value)
.flatMap(Mirror.init(reflecting:))?
.children
.first(where: { [=10=].label == "some" })
.map(\.value)
.flatMap(Mirror.init(reflecting:))?
.children
.first(where: { [=10=].label == "contactId" })?
.value as? String
}
后来,在看到“some”这个词后,我意识到 Mirror's descendant function 可以做完全相同的工作:
func findContactId() -> String? {
let mirror = Mirror(reflecting: self)
let value = mirror.descendant(responseId, "some", "contactId") as? String
return value
}
我有大量的结构,它们都有 responseId: String
并且有一个 属性 与包含 contactId: String
的 responseId
的值同名.
responseId
始终是非可选字符串。
内部对象的 contactId
也是非可选的 String- 所有内部对象都是可选的(在运行时只有一个对象是非零的)
下面是两个例子:
protocol ContainerBase {
var responseId: String { get }
}
struct Container1: ContainerBase {
struct OtherA {
let contactId: String
let cats: [String] // Other info here, not related to OtherB
}
struct OtherB {
let contactId: String
let dogsNum: Int // Other info here, not related to OtherA
}
let responseId: String
let otherA: OtherA? // Optional
let otherB: OtherB? // Optional
}
struct Container2: ContainerBase {
struct AnotherA {
let contactId: String
let passed: Bool // Other info here, not related to AnotherB
}
struct AnotherB {
let contactId: String
let friend: String // Other info here, not related to AnotherA
}
let responseId: String
let anotherA: AnotherA? // Optional
let anotherB: AnotherB? // Optional
}
问题:
如何从 Container1
或 Container2
动态访问 contactId
? (我尝试了非动态方法,每个 ContainerN 结构都有一个额外的函数,里面有一个开关,但这变得很疯狂,因为我有太多这样的结构,我已经犯了一些错别字,忘记了一些情况,导致了错误,......我想象一下可靠的解决方案是“反射”?)。
示例:
例如,如果 Container1
的 responseId
是“otherA”,那么我应该在 属性 otherA
中寻找 contactId
。由于我有几种类型的容器,每种容器都有不同类型的不相关内部对象,因此解决方案不应特定于 Container1 或 Container2,它应该适用于任何 ContainerBase
.
我实现了一个肮脏的代码,但它会导致警告,并且在不生成警告的情况下无法正常工作。另外我认为这不能可靠地工作(这是我的 iOS 应用程序,但奇怪的是这在 linux Swift 中不起作用)。这可能吗?还是编译器故障?
let c1 = Container1(
responseId: "otherA",
otherA: Container1.OtherA(contactId: "123", cats: ["figaro"]),
otherB: nil)
c1.findContactId() // expected "123"
前面的丑陋代码:
extension ContainerBase {
func findContactId() -> String? {
let mirror = Mirror(reflecting: self)
guard let tInnerRes = mirror.children.first(where: { [=14=].label == self.responseId })?.value else { return nil }
// WARN: Conditional cast from 'Any' to 'Optional<Any>' always succeeds
guard let maybeInnerRes = (tInnerRes as? Optional<Any>) else { return nil }
guard let innerRes = maybeInnerRes else { return nil }
let innerMirror = Mirror(reflecting: innerRes)
let contactId = innerMirror.children.first(where: { [=14=].label == "contactId" })?.value as? String
return contactId
}
}
感谢任何帮助
我在 swift 论坛上问了同样的问题(但解释得更好),并从 Alexis Schultz 那里得到了 a great answer:
func findContactId() -> String? {
Mirror(reflecting: self)
.children
.first(where: { [=10=].label == responseId })
.map(\.value)
.flatMap(Mirror.init(reflecting:))?
.children
.first(where: { [=10=].label == "some" })
.map(\.value)
.flatMap(Mirror.init(reflecting:))?
.children
.first(where: { [=10=].label == "contactId" })?
.value as? String
}
后来,在看到“some”这个词后,我意识到 Mirror's descendant function 可以做完全相同的工作:
func findContactId() -> String? {
let mirror = Mirror(reflecting: self)
let value = mirror.descendant(responseId, "some", "contactId") as? String
return value
}