swift修改struct的泛型属性

时间:2017-08-07 22:21:56

标签: swift generics properties mutate

我有一个结构,它有一个泛型属性,对自定义协议有类型约束。该协议是空的,其唯一目的是确保只有某些类型的元素可以存储在这个通用变量中:

protocol MyProtocol {}

struct TypeA: MyProtocol {
    someProperty: String
}

struct TypeB: MyProtocol {
    someOtherProperty: Int
}

var a = TypeA(someProperty: "text")
var b = TypeB(someOtherProperty: 5)

// The following is the actual struct in question:

struct Item {
    var something: Int
    var elementOfTypeAorB: MyProtocol
}

var firstItem = Item(something: 10, elementOfTypeAorB: a)
var secondItem = Item(something: 3, elementOfTypeAorB: b)

当我想要访问我的“基础”结构TypeA或TypeB的属性时,我必须将它们转换为原始类型:

print((secondItem.elementOfTypeAorB as! TypeB).someOtherProperty)  // 5

我现在需要一个func来检查属性是否属于TypeB,如果是,请更改该值,因此函数体可以读取:

if type(of: secondItem.elementOfTypeAorB) == TypeB.self {
    (firstItem.elementOfTypeAorB as! TypeB).someOtherProperty+=5
}

但是我得到一条错误信息:变异运算符的左侧有不可变类型'Int'

如果我将TypeA和TypeB结构更改为类,我可以这样做:

if type(of: secondItem.elementOfTypeAorB) == TypeB.self {
    var modify = (secondItem.elementOfTypeAorB as! TypeB)
    modify.someOtherProperty+=5
}

由于类是引用类型,secondItem的原始someOtherProperty将被更改,但即使a.someOtherProperty也会被更改(后者无关紧要,因为a和b这里只是辅助变量。

但是如果我想留在结构域中,我发现改变泛型elementOfTypeAorB属性的唯一方法是将它们向下转换为新变量,更改此变量并将整个变量写回更高 - level struct,如:

secondItem.elementOfTypeAorB = modify

这很好用,但在我的实际项目中,TypeA和TypeB结构不仅包含一个属性,所以每次我想改变其中一个必须复制整个结构然后再替换使用此修改后的副本的整个结构对我来说似乎相当昂贵。

那么,还有另一种方法可以改变我刚刚没有遇到的通用结构的属性吗?

1 个答案:

答案 0 :(得分:1)

你必须执行一个可选的演员:

if var elementA = elementOfTypeAorB as? TypeA {
    elementA.someProperty = ...
    elementOfTypeAorB = elementA
} else if var elementB = elementOfTypeAorB as? TypeB {
    elementB.someOtherProperty = ...
    elementOfTypeAorB = elementA
}

此外,您应该使用type(of: value) == Type而不是使用value is Type检查类型。

或者,考虑使用带有关联值的枚举。它更适合您的使用案例,因为协议应该用于定义可用于与实例交互的接口。

enum AorB {
    case a(TypeA)
    case b(TypeB)
}

这允许您将变量的类型限制为TypeA和TypeB。

然后,您可以使用switch caseif case语句来解包该值:

switch elementOfTypeAorB {
    case .a(var elementA):
        elementA.someProperty = ...
        elementOfTypeAorB = .a(elementA)
    case .b(var elementB):
        elementB.someOtherProperty = ...
        elementOfTypeAorB = .b(elementB)
}