Access a struct property by its name as a string i

2019-04-08 01:41发布

问题:

Let's say I have the following struct in Swift:

struct Data {
   let old: Double
   let new: Double
}

Now I have a class with an array of Data structs:

class MyClass {
   var myDataArray: [Data]
}

Now let's say I want to calculate the average of either the old or the new values:

func calculateAverage(oldOrNew: String) -> Double {
   var total = 0.0
   count = 0

   for data in myDataArray {
      total += data.oldOrNew
      count++
   }

   return total / Double(count)
}

And then:

let oldAverage = calculateAverage("old")
let newAverage = calculateAverage("new")

But this obviously doesn't work, since oldOrNew is not a member of my struct.

How can I access old or new from "old" or "new" ?

回答1:

What about this "reflection-less" solution?

struct Data {
    let old: Double
    let new: Double

    func valueByPropertyName(name:String) -> Double {
        switch name {
        case "old": return old
        case "new": return new
        default: fatalError("Wrong property name")
        }
    }
}

Now you can do this

let data = Data(old: 0, new: 1)

data.valueByPropertyName("old") // 0
data.valueByPropertyName("new") // 1


回答2:

You're looking for key-value-coding (KVC) that is accessing properties by key (path).

Short answer: A struct does not support KVC.

If the struct is not mandatory in your design use a subclass of NSObject there you get KVC and even operators like @avg for free.

class MyData : NSObject {
  @objc let old, new: Double

  init(old:Double, new:Double) {
    self.old = old
    self.new = new
  }
}

let myDataArray : NSArray = [MyData(old: 1, new: 3), MyData(old:5, new: 9), MyData(old: 12, new: 66)]

let averageOld = myDataArray.value(forKeyPath:"@avg.old")
let averageNew = myDataArray.value(forKeyPath: "@avg.new")

Edit: In Swift 4 a struct does support Swift KVC but the operator @avg is not available



回答3:

You wouldn't access a struct property by name in Swift any more than you would in C++. You'd provide a block.

Extemporaneous:

func calculateAverage(getter: (Data) -> Double) {
    ... total += getter(data) ...
}

...

calculateAverage({$0.old})
calculateAverage({$0.new})

Possibly with average {$0.old} being a more natural syntax — the verb isn't really helpful and if you're asserting what it is, not what the computer should do, then omitting the brackets looks fine.



标签: struct swift2