清楚地从更大的结构更新一些嵌套结构的方法

问题描述 投票:0回答:3

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

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

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 param来编写它:

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

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

这是有效的,但正如您所看到的,我们需要在通过ref传递表达式之前解开表达式“company.employees [1]”。如果提供的密钥没有此类员工,则强制解包可能会产生运行时错误。

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

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

这也有效,但是这段代码很难看,因为我们需要重复两次表达式“company.employees [1]”。

所以问题是:有没有办法摆脱这种重复?

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

swift
3个回答
1
投票

根据你的评论,比如

我首先想要的只是引用一个更大结构的子结构,因此处理子结构的代码部分可能对这个子结构位于更大结构中的位置一无所知。

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

我认为你想要的是一个通用的更新功能。在社区中,这是被称为withhttps://forums.swift.org/t/circling-back-to-with/2766)的效用函数族的一部分

在这种情况下你需要的版本是guard基本上nils,所以我建议像

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 })

Note

如果你想抽象出如何访问员工而不是company,那么keypaths也是一个选项:)


1
投票

您需要隐藏实现并让结构使用特定的错误处理策略处理逻辑,例如抛出错误或根据成功返回true / false或者忽略任何问题。我不知道Int键代表什么,但在这里我猜它是某种类型的ID,所以将它添加到Company结构中

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

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]) { ... }
© www.soinside.com 2019 - 2024. All rights reserved.