1

我有 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)

任何想法都会有所帮助

4

1 回答 1

1

首先,可能是一个转录错误,但id需要对其进行设置才能有意义。

protocol A {
    var id: String { get set }  // <=== Add set
}

对于二传手,有两个问题;一个很清楚,另一个有点微妙。明显的问题是,即使这有效,它也无济于事:

        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 的类型”,这不是一回事。要使此代码正常工作,案例需要是 like.a1(A)而不是.a1(A1).

Swift 确实允许您读取根植于该类型所遵循的协议的 KeyPath。但它不允许你写完它。请参阅WritableKeyPath + inout 占位符类型的参数无法编译以进行简短讨论。

你会写“符合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 枚举通常不如我们希望将它们与结构进行比较的强大.但有时枚举仍然是最好的。)

于 2021-07-07T17:28:57.830 回答