Swift - 改变枚举关联类型属性的值
Swift - Mutate values of enum associate type properties
我有 2 个结构符合一个协议。我创建了具有关联类型的枚举作为结构类型。当我尝试使用动态成员查找访问协议类型 A 的属性时,getter 工作正常。但是 Set 方法抛出错误:具有根类型 'A' 的密钥路径不能应用于 'A1'/'A2'
类型的基础
有时我也会遇到这个错误:无法通过下标赋值:'a' is immutable
import Foundation
protocol A {
var id: String { get }
}
struct A1 : A {
var id: String
var type: String
}
struct A2 : A {
var id: String
var test: String
}
@dynamicMemberLookup
enum AType {
case a1(A1)
case a2(A2)
}
extension AType {
subscript<T>(dynamicMember keyPath: WritableKeyPath<A, T>) -> T {
get {
switch self{
case .a1(let a):
return a[keyPath: keyPath]
case .a2(let a):
return a[keyPath: keyPath]
}
}
set {
switch self{
case .a1(var a):
a[keyPath: keyPath] = newValue
case .a2(var a):
a[keyPath: keyPath] = newValue
}
}
}
}
let a1struct = A1(id: "123", type: "Test")
var atype = AType.a1(a1struct)
print(atype.id)
任何想法都会有所帮助
首先,可能是转录错误,但 id
需要设置才能使它有意义。
protocol A {
var id: String { get set } // <=== Add set
}
对于setter,有两个问题;一个非常清楚,另一个有点微妙。明显的问题是,即使这有效,它也无能为力:
case .a1(var a):
a[keyPath: keyPath] = newValue
这会制作关联数据的副本,然后修改该副本,然后将其丢弃。你需要的是(虽然这段代码也不起作用):
case .a1(var a):
a[keyPath: keyPath] = newValue
self = .a1(a)
您需要修改a
,然后创建一个新的枚举值来保存它。
更微妙的问题是类型WritableKeyPath<A, T>
。这表示“植根于 A 类型的可写 KeyPath”。你的意思是“一个可写的 KeyPath 植根于 符合 到 A 的类型”,这不是一回事。为了让这段代码起作用,案例需要像 .a1(A)
而不是 .a1(A1)
.
Swift 确实允许您读取植根于该类型符合的协议的 KeyPath。但它不会让你通过它来写。请参阅 WritableKeyPath + inout argument of placeholder type doesn't compile 进行简短讨论。
你写“符合 A 的东西”的方式是:
subscript<Container: A, T>(dynamicMember keyPath: WritableKeyPath<Container, T>) -> T {
但是,这行不通,因为 Container 的类型由调用者决定,并且它必须接受任何传递的符合 A 的类型(您不能这样做)。
我还没有想出如何在没有 as!
演员表的情况下解决这个问题,但可以通过这种方式完成:
set {
switch self{
case .a1(var a as A):
a[keyPath: keyPath] = newValue
self = .a1(a as! A1)
case .a2(var a as A):
a[keyPath: keyPath] = newValue
self = .a2(a as! A2)
}
}
(我可能会建议重新考虑在这里使用枚举,看看是否可以直接使用 A1 和 A2 并使用 is
而不是 switch
来区分它们。Swift 与结构相比,枚举通常不如我们希望的那么强大。但有时枚举仍然是最好的。)
我有 2 个结构符合一个协议。我创建了具有关联类型的枚举作为结构类型。当我尝试使用动态成员查找访问协议类型 A 的属性时,getter 工作正常。但是 Set 方法抛出错误:具有根类型 'A' 的密钥路径不能应用于 'A1'/'A2'
类型的基础有时我也会遇到这个错误:无法通过下标赋值:'a' is immutable
import Foundation
protocol A {
var id: String { get }
}
struct A1 : A {
var id: String
var type: String
}
struct A2 : A {
var id: String
var test: String
}
@dynamicMemberLookup
enum AType {
case a1(A1)
case a2(A2)
}
extension AType {
subscript<T>(dynamicMember keyPath: WritableKeyPath<A, T>) -> T {
get {
switch self{
case .a1(let a):
return a[keyPath: keyPath]
case .a2(let a):
return a[keyPath: keyPath]
}
}
set {
switch self{
case .a1(var a):
a[keyPath: keyPath] = newValue
case .a2(var a):
a[keyPath: keyPath] = newValue
}
}
}
}
let a1struct = A1(id: "123", type: "Test")
var atype = AType.a1(a1struct)
print(atype.id)
任何想法都会有所帮助
首先,可能是转录错误,但 id
需要设置才能使它有意义。
protocol A {
var id: String { get set } // <=== Add set
}
对于setter,有两个问题;一个非常清楚,另一个有点微妙。明显的问题是,即使这有效,它也无能为力:
case .a1(var a):
a[keyPath: keyPath] = newValue
这会制作关联数据的副本,然后修改该副本,然后将其丢弃。你需要的是(虽然这段代码也不起作用):
case .a1(var a):
a[keyPath: keyPath] = newValue
self = .a1(a)
您需要修改a
,然后创建一个新的枚举值来保存它。
更微妙的问题是类型WritableKeyPath<A, T>
。这表示“植根于 A 类型的可写 KeyPath”。你的意思是“一个可写的 KeyPath 植根于 符合 到 A 的类型”,这不是一回事。为了让这段代码起作用,案例需要像 .a1(A)
而不是 .a1(A1)
.
Swift 确实允许您读取植根于该类型符合的协议的 KeyPath。但它不会让你通过它来写。请参阅 WritableKeyPath + inout argument of placeholder type doesn't compile 进行简短讨论。
你写“符合 A 的东西”的方式是:
subscript<Container: A, T>(dynamicMember keyPath: WritableKeyPath<Container, T>) -> T {
但是,这行不通,因为 Container 的类型由调用者决定,并且它必须接受任何传递的符合 A 的类型(您不能这样做)。
我还没有想出如何在没有 as!
演员表的情况下解决这个问题,但可以通过这种方式完成:
set {
switch self{
case .a1(var a as A):
a[keyPath: keyPath] = newValue
self = .a1(a as! A1)
case .a2(var a as A):
a[keyPath: keyPath] = newValue
self = .a2(a as! A2)
}
}
(我可能会建议重新考虑在这里使用枚举,看看是否可以直接使用 A1 和 A2 并使用 is
而不是 switch
来区分它们。Swift 与结构相比,枚举通常不如我们希望的那么强大。但有时枚举仍然是最好的。)