代码之家  ›  专栏  ›  技术社区  ›  learningtech

无法设置类型为接口{}的结构的字段

  •  -1
  • learningtech  · 技术社区  · 6 年前

    我一直在为“反思”计划而挣扎。下面的代码符合我的预期:

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

    在过去的几个小时里,我尝试了很多其他的事情,比如尝试去做 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

    所以我的问题是,当结构类型化为接口时,如何设置结构的字段?


    更新

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

    2 回复  |  直到 6 年前
        1
  •  3
  •   Cerise Limón    6 年前

    试试这个:

    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 .

        2
  •  0
  •   learningtech    6 年前

    纯粹出于运气,我终于找到了工作。

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

    如果有人能给出一个更好的答案来澄清我的困惑,那就太好了。我学歌浪一直很辛苦。