1

我一直在努力使用反射包。下面的代码符合我的预期:

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()即删除类型断言,则会收到错误消息:

恐慌:反映:在接口 Value 上调用 reflect.Value.FieldByName “字段:= elem.FieldByName(fieldName)

reflect.TypeOf()在过去的几个小时里,我在其他一些变量上尝试了很多其他的事情,比如尝试做reflect.Indirect()等等……但没有成功。

我读过其他一些类似的问题:

reflect: 在 ptr Value 上调用 reflect.Value.FieldByName

使用接口的字段类型设置结构字段

Golang反射:无法设置包装结构的接口字段

他们似乎表明我对指针或接口的工作方式没有很好的理解。

所以我的问题是,当结构被键入为接口时,我该如何设置结构的字段?


更新

我发布了一个解决方案作为答案,但我对它是否是正确或安全的做事方式没有信心。我希望有人能解释一下,或者发布一个更好的解决方案。

4

2 回答 2

3

尝试这个:

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)

请注意,调用直接传递切片,而不是使用指向切片的指针。不需要指针并增加额外的复杂性。

在操场上运行它

于 2018-09-01T03:42:01.923 回答
0

幸运的是,我终于得到了一些工作。

我拼凑了一堆随机阅读的东西,几乎没有押韵或理由。我什至尝试阅读 Golang 网站上的反射定律,但我认为我没有很好地理解它与为什么我不能设置类型为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)
}

如果有人可以发布一个更好的答案来消除我的困惑,那就太好了。我一直很难学习 golang。

于 2018-09-01T02:24:19.810 回答