Не удается назначить свойство экземпляра, которое возвращается из подстрочного индекса, доступного только для получения

#swift

Вопрос:

Я возвращаю экземпляр из подстрочного индекса, доступного только для получения, но не могу присвоить свойство возвращаемого экземпляра

 struct Student {  var id: String  var attendance = 0  var absence = 0 }  class StudentStore {  var students = [Student]()  subscript(id: String) -gt; Student? {  students.first(where: { $0.id == id } )  } }  var studentStore = StudentStore()  var newStudent = Student(id: "2014901001") studentStore.students.append(newStudent)  studentStore["2014901001"]?.attendance = 1 // error: Cannot assign to property: subscript is get-only  

Комментарии:

1. Ну нет, потому Student что это структура и имеет семантику значения. Как насчет того Student , чтобы вместо этого провести занятие?

Ответ №1:

В подписке нет set тер. То, что у вас есть, — это неявный get тер, который является краткой формой

 subscript(id: String) -gt; Student? {  get {  students.first(where: { $0.id == id } )  } }  

Но в любом случае невозможно обновить элемент типа значения в массиве с помощью подписки на ключ.

Альтернативой является добавление функции обновления в магазин , которая принимает id , a keyPath и a value

 func updateStudentlt;Valuegt;(withID id: String, keyPath: WritableKeyPathlt;Student,Valuegt;, value: Value) {  guard let index = students.firstIndex(where: {$0.id == id}) else { return }  students[index][keyPath: keyPath] = value }  

Получение индекса обязательно для того, чтобы иметь возможность напрямую изменять элемент в массиве.


Затем вы можете заменить

 studentStore["2014901001"]?.attendance = 1  

с

 studentStore.updateStudent(withID: "2014901001", keyPath: .attendance, value:1)  

Комментарии:

1. Да, я забыл, что структура-это тип значения, но также нет необходимости в наборе в индексе, потому что я просто хочу получить экземпляр Student с помощью get, а затем назначить свойства экземпляра с помощью синтаксиса точек.

Ответ №2:

Когда вы это сделаете

 studentStore["2014901001"]?.attendance = 1  

Вы пытаетесь установить один из Student объектов в students массиве, не так ли?

Однако средство получения индексов не возвращает (ссылку на) один из объектов в students массиве. Из-за семантики значений Student структуры вы возвращаете копию Student объекта, находящегося в students массиве. Вы не сможете достичь того, чего хотите, если просто установите attendance эту копию. Поэтому компилятор Swift компилирует ваш код примерно так:

 let copy = studentStore["2014901001"] copy?.attendance = 1 studentStore["2014901001"] = copy  

Вы можете видеть, что он снова устанавливает сеттер с измененной копией. Но в вашем индексе нет установщика, из-за чего возникает ошибка.

Чтобы решить эту проблему, вы можете создать Student класс, который имеет ссылочную семантику. Это позволяет вам возвращать Student объект, который находится в students массиве, позволяя вызывающему подстрочному индексу устанавливать свою посещаемость.

 class Student {  var id: String  var attendance = 0  var absence = 0    init(id: String, attendance: Int = 0, absence: Int = 0) {  self.id = id  self.attendance = attendance  self.absence = absence  } }  

В качестве альтернативы вы можете добавить сеттера. Однако это позволило бы допустить подобную чепуху:

 studentStore["1"] = Student(id: "2")  

Вам нужно решить, что вы хотите, чтобы здесь произошло.

Комментарии:

1. Я забыл, что структура-это тип значения. Вместо этого я использовал класс, и это работает.

Ответ №3:

Ошибка показывает, что именно вам нужно изменить. Потому subcript что вернется что-то подобное:

 let updateStudent = studentStore["studentStore"]  

Вы не можете измениться с let

Так что обновите это:

 if var updateStudent = studentStore["studentStore"] {  updateStudent.attendance = 1 }  

Комментарии:

1. Нет, из-за семантики значений это не обновит ученика в магазине. Вместо этого он обновит копию студента.

2. Да, ты прав. Я просто подумал, что он хочет исправить ошибку. Таким образом, потребуется еще один шаг для обновления Student объекта с заданным идентификатором.