【问题标题】:Can't set field of a struct that is typed as an interface{}无法设置类型为接口的结构的字段{}
【发布时间】:2019-02-06 23:58:27
【问题描述】:

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

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

panic: reflect: 在接口 Value 上调用 reflect.Value.FieldByName 在线“字段:= elem.FieldByName(fieldName)

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

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

reflect: call of reflect.Value.FieldByName on ptr Value

Set a struct field with field type of a interface

Golang reflection: Can't set fields of interface wrapping a struct

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

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


更新

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

【问题讨论】:

    标签: go go-reflect


    【解决方案1】:

    试试这个:

    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)
    

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

    Run it on the Playground.

    【讨论】:

    • 稍后,我想在ModifyIt() 函数中为v 添加一些新元素。 main() 函数中的变量 p 将保留 v 在我调用 v = append(v,Person{Name:"Sara"}) 之前的行之前的任何值。如果我想让main() 中的p 显示ModifyIt() 中添加到v 的新项目,是否需要将p 作为指针传递给ModifyIt,然后按照我发布的解决方案进行操作?
    • 根据我的评论,我认为是否将p 作为指针传递与我在@ 中关于vappendp 的最终问题无关987654341@。我将不得不更多地玩耍以了解它是如何工作的
    • 我不确定您要做什么。例如,您要添加什么?描述您想要的内容的最佳方式是使用特定类型(汽车、人员和这些类型的切片)的代码来更新问题。我无法从问题中的代码或您的答案中推导出它。
    • 如果要向父切片添加元素,则将切片指针传递给 ModifyIt 并将 ModifyIt 的第一行更改为v := reflect.ValueOf(slice).Elem()。使用v.Set(reflect.Append(v, newValue)) 附加到切片。
    • 谢谢,您的建议很有道理,但我仍然对刚刚在此处发布的另一个入门级问题摸不着头脑stackoverflow.com/questions/52125578/…
    【解决方案2】:

    幸运的是,我终于找到了工作。

    我把我读到的一堆随机的东西拼凑起来,几乎没有押韵或理由。我什至尝试阅读 Golang 网站上的反射定律,但我认为我没有很好地理解它与为什么我不能设置类型为 interface{} 的变量的关系。总的来说,我仍然不明白我做了什么。

    我下面的解决方案中到处都是 cmets,表明我的困惑,以及对我是否正确或安全地做事缺乏信心。

    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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-14
      • 1970-01-01
      • 2021-09-25
      • 1970-01-01
      • 2020-11-09
      • 2016-02-01
      • 2021-06-16
      相关资源
      最近更新 更多