从较大的结构更新嵌套结构的清晰方法

时间:2019-03-13 10:47:47

标签: swift

假设我们有一些具有多个嵌套级别的复杂结构(为简单起见,在示例中将仅为一个级别,但可以有更多级别)。

示例。我们有一个数据结构:

struct Company {
    var employee: [Int: Employee]
}

struct Employee {
    var name: String
    var age: Int
}

var company = Company(employee: [
    1: Employee(name: "Makr", age: 25),
    2: Employee(name: "Lysa", age: 30),
    3: Employee(name: "John", age: 28)
    ])

现在,我们想创建一个函数来更新公司的某些员工。我们可以使用inout参数来写它:

func setAge(_ age: Int, forEmployee employee: inout Employee) {
    employee.age = age  
}

setAge(26, forEmployee: &company.employees[1]!)

这有效,但是正如您所看到的,我们需要先将表达式“ company.employees [1]”解开,然后再传递给引用。如果没有提供密钥的员工,则这种强制拆包操作可能会导致运行时错误。

所以我们需要检查员工是否存在:

if company.employees[1] != nil {
    setAge(26, forEmployee: &company.employees[1]!)
}

这也可行,但是这段代码有点丑陋,因为我们需要重复两次表达'company.employees [1]'。

所以问题是:有什么方法可以消除这种重复?

我试图在修改函数中使用可选的inout参数,但无法使其正常工作。

3 个答案:

答案 0 :(得分:1)

您需要隐藏实现,并让结构体使用特定的错误处理策略来处理逻辑,例如引发错误或根据成功仅返回true / false或仅忽略任何问题。我不知道Int键代表什么,但在这里我猜它是某种ID,因此请将其添加到Company struct

mutating func setAge(_ age: Int, forId id: Int) -> Bool {
    if employee.keys.contains(id) {
        employee[id]?.age = age
        return true
    }
    return false
}

答案 1 :(得分:1)

我只需将扩展名添加到Employee即可设置员工的age

extension Employee {
    mutating func setAge(_ age: Int) {
        self.age = age
    }
}

然后,您可以使用可选的链接进行调用。因此,如果键1的值不存在,则什么也不会发生,并且代码会继续运行

company.employee[1]?.setAge(26)

编辑:

如果您的目标只是更改某些属性然后返回对象,则只需创建一个带有可选参数并返回可选值的方法

func setAge(_ age: Int, forEmployee employee: inout Employee?) -> Employee? {
    employee?.age = age
    return employee
}

if let employee = setAge(26, forEmployee: &company.employees[1]) { ... }

答案 2 :(得分:1)

根据您的评论,例如

  

首先,我想要引用的是较大结构的子结构,因此处理该子结构的代码部分可能不知道该子结构在较大结构中的位置。

>

  

如果我可以创建一个本地inout var,那将是理想的选择。就像var雇员一样:inout Employee? = company.employee [1] {//与该员工做我想做的事}。

我认为您想要的是通用更新功能。在社区中,这是实用程序功能家族的一部分,称为withhttps://forums.swift.org/t/circling-back-to-with/2766

在这种情况下,您需要的版本基本上是guard上的nil s版本,因此我建议使用类似的

func performUpdateIfSome <T> (_ value: inout T?, update: (inout T) throws -> Void) rethrows {
  guard var _value = value else { return }
  try update(&_value)
  value = _value
}

使用此实用程序,您将可以完成

performUpdateIfSome(&company.employees[1], update: { $0.age = 26 })

注意

如果您想抽象出如何访问员工而不是company,那么也可以选择键路径:)