Не могу установить поле структуры, которая напечатана как интерфейс {} - PullRequest
0 голосов
/ 01 сентября 2018

Я боролся с отражающим пакетом. Этот код ниже делает то, что я ожидаю:

package main

import (
  "reflect"
  "log"
)
type Car struct {
  Model string
}
type Person struct {
  Name string
  Cars []Car
}

func ModifyIt(parent interface{},fieldName string, val interface{}) {
  slice := reflect.ValueOf(parent).Elem()
  nth := slice.Index(0)
  //row := nth.Interface() // this line causes errors
  row := nth.Interface().(Person)
  elem := reflect.ValueOf(&row).Elem()
  field := elem.FieldByName(fieldName)
  log.Println(field.CanSet())

}

func main() {

  p := []Person{Person{Name:"john"}}
  c := []Car{Car{"corolla"},Car{"jetta"}}

  ModifyIt(&p,"Cars",&c)
}

Однако, если я заменяю строку row := nth.Interface().(Person) на row := nth.Interface(), то есть удаляю утверждение типа, я получаю ошибку:

паника: отражение: вызов рефлекса. Значение.FieldByName в интерфейсе Значение on line "field: = elem.FieldByName (fieldName)

В последние несколько часов я пробовал множество других вещей, например, пытался сделать reflect.TypeOf(), reflect.Indirect() и т. Д. ... для некоторых других переменных, но безуспешно.

Я читал некоторые другие вопросы, подобные этим:

отражение: вызов рефлекса. Значение.FieldByName в ptr Значение

Установить поле структуры с типом поля интерфейса

Отражение Голанга: Невозможно установить поля интерфейса, обертывающие структуру

Кажется, они говорят о том, что я плохо понимаю, как работают указатели или интерфейсы.

Итак, мой вопрос: как мне настроить поле структуры, когда структура вводится как интерфейс?


UPDATE

Я отправил решение в качестве ответа, но я не уверен, является ли это правильным или безопасным способом выполнения действий. Я надеюсь, что кто-то может объяснить или опубликовать лучшее решение.

Ответы [ 2 ]

0 голосов
/ 01 сентября 2018

Попробуйте это:

func ModifyIt(slice interface{}, fieldName string, newVal interface{}) {
    // Create a value for the slice.
    v := reflect.ValueOf(slice)

    // Get the first element of the slice.
    e := v.Index(0)

    // Get the field of the slice element that we want to set.
    f := e.FieldByName(fieldName)

    // Set the value!
    f.Set(reflect.ValueOf(newVal))
}

Назовите это так:

p := []Person{Person{Name: "john"}}
c := []Car{Car{"corolla"}, Car{"jetta"}}
ModifyIt(p, "Cars", c)

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

Запустите его на игровой площадке .

0 голосов
/ 01 сентября 2018

Из чистой удачи я наконец-то получил работу.

Я собрал кучу случайных вещей, которые я прочитал, с небольшим рифмой или рассудком. Я даже пытался прочитать Законы Отражения на сайте Голанга, но я не думаю, что хорошо понимаю, как это связано с тем, почему я не могу установить переменные, набранные как interface{}. В общем, я до сих пор не понимаю, что я сделал.

Мое решение, приведенное ниже, изобилует комментариями, указывающими на мое замешательство и отсутствие уверенности в том, правильно ли я поступил, или безопасно.

package main

import (
  "reflect"
  "log"
)
type Car struct {
  Model string
}
type Person struct {
  Name string
  Cars []Car
}

func ModifyIt(parent interface{},fieldName string, val interface{}) {
  log.Println(parent)
  slice := reflect.ValueOf(parent).Elem()
  nth := slice.Index(0)
  row := nth.Interface()
  log.Println(nth.CanSet()) // I can set this nth item

  // I think I have a to make a copy, don't fully understand why this is necessary
  newitem := reflect.New(reflect.ValueOf(row).Type())
  newelem := newitem.Elem()
  field := newelem.FieldByName(fieldName)

  // I need to copy the values over from the old nth row to this new item
  for c:=0; c<nth.NumField(); c++ {
    newelem.Field(c).Set(reflect.Indirect(nth.Field(c)))
  }

  // now I can finally set the field for some reason I don't understand
  field.Set(reflect.ValueOf(val).Elem())

  // now that newitem has new contents in the field object, I need to overwrite the nth item with new item
  // I don't know why I'm doing it, but I'll do it
  // I also don't fully understand why I have to use Indirect sometimes, and not other times...it seems interchangeable with ValueOf(something).Elem(), I'm confused....
  nth.Set(reflect.Indirect(newitem))

}

func main() {

  p := []Person{Person{Name:"john"}}
  c := []Car{Car{"corolla"},Car{"jetta"}}

  ModifyIt(&p,"Cars",&c)
  // now parent is up to date, although I have no idea how I got here.
  log.Println(p)
}

Если кто-нибудь сможет опубликовать лучший ответ, который прояснит мою путаницу, это будет здорово. Мне действительно тяжело было учиться голангу.

...