简体   繁体   中英

How to create an array with mixed structs with different properties in Swift?

I am trying to create an array of multiple structs, I have created a protocol to do so that allows me to get and set similar properties but not when properties are different. My current code right now is below:

structs:

struct A: Pr {
poll: String
res: Int
}

struct B: Pr {
poll:String
ont: String
}

Protocol:

protocol Pr {
   var poll:String { get set }
}

var type without initialization:

var field:[Pr]

After sanitizing your code a bit:

protocol Pr {
   var poll:String { get set }
}

struct A: Pr {
  var poll: String
  var res: Int
}

struct B: Pr {
  var poll:String
  var ont: String
}

var field: [Pr]

If I understand what you want is something like this:

// Just an example to fill array with something for a demo:
let a = A(poll: "pollA", res: 1)
let b = B(poll: "pollB", ont: "ont")

field = [Pr]()
field.append(a)
field.append(b)

// The actual solution
for pr in field {

    print(pr.poll)

    switch pr {
    case let pr as A:
        print(pr.res)
    case let pr as B:
        print(pr.ont)
    default:
        print("no more properties")
    }
}

That is: you can cast each item in array as given type to get properties specific to the type. This solves the GET problem. Set is more complicated, since you have an array and structs, which is copied when modified. So technically you can change them, but it won't be the same instance. The most simple way to resolve this, is to have a function that returns an array of modified [Pr] objects, which you can assign back to field :

func modify() -> [Pr] {

    var result = [Pr]()

    for pr in field {

        switch pr {
        case let pr as A:
            let a = A(poll: pr.poll + "changed", res: pr.res + 1)
            result.append(a)
        case let pr as B:
            let b = B(poll: pr.poll + "changed", ont: pr.ont + "changed")
            result.append(b)
        default:
            print("skip")
        }
    }

    return result
}

field = modify()

But this may not be a good solution if you are dealing with tons of data, lots of copies, etc, etc... So a more specific use case would be needed to make the answer more useful.

You have two problems:

  1. An array of structs that conform to the Pr protocol is not an array of A and B objects:

  2. The Array and Struct types are both value types, so you can't cast an entry from your array to a specific concrete type without making a copy.

This code solves problem 1, but not problem 2:

(field[0] as? A)?.res = 5

I'm not sure if there is a solution to your problem, other than converting your A and B to class (reference type) objects. This code works:

protocol Pr {
   var poll:String { get set }
}

class A: Pr, CustomStringConvertible {
    var poll: String
    var res: Int
    
    init(poll: String, res: Int) {
        self.poll = poll
        self.res = res
    }
    
    var description: String {
        return ("A(poll: \(poll), res:\(res))")
    }
}

class B: Pr, CustomStringConvertible {
    var poll:String
    var ont: String

    init(poll: String, ont: String) {
        self.poll = poll
        self.ont = ont
    }
    var description: String {
        return ("B(poll: \(poll), ont:\(ont))")
    }
}

var field:[Pr] = [A(poll:"h", res:3), B(poll:"c", ont:"o"),]
field[0].poll = "p"
field[1].poll = "q"

(field[0] as? A)?.res = 5

(field[1] as? B)?.ont = "z"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM