【问题标题】:Iterate through the fields of a struct in Go遍历 Go 中结构的字段
【发布时间】:2013-09-20 21:39:55
【问题描述】:

基本上,(据我所知)迭代struct 的字段值的唯一方法是这样的:

type Example struct {
    a_number uint32
    a_string string
}

//...

r := &Example{(2 << 31) - 1, "...."}:
for _, d:= range []interface{}{ r.a_number, r.a_string, } {
  //do something with the d
}

我想知道,是否有更好、更通用的方法来实现[]interface{}{ r.a_number, r.a_string, },所以我不需要单独列出每个参数,或者,是否有更好的方法来循环遍历结构?

我试图查看reflect 包,但我碰壁了,因为我不知道在检索到reflect.ValueOf(*r).Field(0) 后该做什么。

谢谢!

【问题讨论】:

标签: go go-reflect


【解决方案1】:

使用Field(i) 检索字段的reflect.Value 后,您可以获得 通过调用Interface() 从中获取接口值。所述接口值则表示 字段的值。

没有将字段的值转换为具体类型的功能, 如您所知,go 中没有泛型。因此,没有签名为GetValue() T 的函数 T 是该字段的类型(当然会根据字段而变化)。

你可以在 go 中实现的最接近的是GetValue() interface{},这正是reflect.Value.Interface() 优惠。

以下代码说明了如何获取结构中每个导出字段的值 使用反射(play):

import (
    "fmt"
    "reflect"
)

func main() {
    x := struct{Foo string; Bar int }{"foo", 2}

    v := reflect.ValueOf(x)

    values := make([]interface{}, v.NumField())

    for i := 0; i < v.NumField(); i++ {
        values[i] = v.Field(i).Interface()
    }

    fmt.Println(values)
}

【讨论】:

  • 是的,因为 Go 不需要泛型。咳咳 :-) 有没有办法获取字段的类型?
  • 通过reflect.Value.Type(),是的。但请注意,类型不是 go 中的一等公民,因此您只能使用 reflect 实例化该类型的新值。
  • 如果您尝试访问未导出的私有字段,v.Field(i).Interface() 会出现恐慌。小心点:)
  • 使用v.Field(i).CanInterface() 可以避免未导出字段的恐慌。
  • 如何获取字段名?
【解决方案2】:

如果你想遍历结构的字段和值,那么你可以使用下面的 Go 代码作为参考。

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Fname  string
    Lname  string
    City   string
    Mobile int64
}

func main() {
    s := Student{"Chetan", "Kumar", "Bangalore", 7777777777}
    v := reflect.ValueOf(s)
    typeOfS := v.Type()

    for i := 0; i< v.NumField(); i++ {
        fmt.Printf("Field: %s\tValue: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface())
    }
}

在playground运行

注意:如果你的结构体中的字段没有被导出,那么v.Field(i).Interface() 将导致恐慌panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

【讨论】:

  • 这是一个很好的答案
  • 这是我一直在寻找的答案。
  • 谢谢,这太完美了!
【解决方案3】:

Go 1.17(2021 年第三季度)应该添加一个新选项,通过 commit 009bfea 和 CL 281233,修复 issue 42782。

reflect:添加 VisibleFields 功能

在编写反映结构类型的代码时,通常需要了解完整的结构字段集,包括由于嵌入匿名成员而可用的字段,同时排除由于与它们处于同一级别而被删除的字段另一个具有相同名称的字段。

这样做的逻辑并不复杂,但有点微妙,容易出错。

此 CL 向 reflect 包添加了一个新的 reflect.VisibleFields() 函数,该函数返回适用于给定结构类型的完整有效字段集。

fields := reflect.VisibleFields(typ)
for j, field := range fields {
    ...
}

例子,

type employeeDetails struct {
    id          int16
    name        string
    designation string
}
func structIterator() {
    fields := reflect.VisibleFields(reflect.TypeOf(struct{ employeeDetails }{}))
    for _, field := range fields {
        fmt.Printf("Key: %s\tType: %s\n", field.Name, field.Type)
    }
}

【讨论】:

  • 我认为它已经包含在 1.17 中。感谢分享
  • @arif 确实是:golang.org/doc/go1.17#reflect
  • 奇怪。我刚刚下载了 1.17 并尝试使用它,但它找不到该功能。也许我错过了一些愚蠢的东西。
  • 我只需要使用reflect.VisibleFields。我正在尝试编辑,但似乎队列已满。
  • @arif 谢谢你的例子。
【解决方案4】:

也许为时已晚 :))) 但还有另一种解决方案,您可以找到结构的键和值并对其进行迭代

package main

import (
    "fmt"
    "reflect"
)

type person struct {
    firsName string
    lastName string
    iceCream []string
}

func main() {
    u := struct {
        myMap    map[int]int
        mySlice  []string
        myPerson person
    }{
        myMap:   map[int]int{1: 10, 2: 20},
        mySlice: []string{"red", "green"},
        myPerson: person{
            firsName: "Esmaeil",
            lastName: "Abedi",
            iceCream: []string{"Vanilla", "chocolate"},
        },
    }
    v := reflect.ValueOf(u)
    for i := 0; i < v.NumField(); i++ {
        fmt.Println(v.Type().Field(i).Name)
        fmt.Println("\t", v.Field(i))
    }
}
and there is no *panic* for v.Field(i)

【讨论】:

  • 您好,欢迎来到 Stack Overflow!请拨打tour。感谢您提供答案(问题的年龄无关紧要)。你能多解释一下你的答案是如何解决问题的吗?
【解决方案5】:

使用这个:

type x struct {
    Id  int
    jsj int
}
func main() {
    x2 := x{jsj: 10, Id: 5}
    v := reflect.ValueOf(x2)
    for i := 0; i < v.NumField(); i++ {
        fmt.Println(v.Field(i))
    }
}

====>10

====>5

【讨论】:

    【解决方案6】:

    采取Chetan Kumar解决方案,以防您需要申请map[string]int

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type BaseStats struct {
        Hp           int
        HpMax        int
        Mp           int
        MpMax        int
        Strength     int
        Speed        int
        Intelligence int
    }
    
    type Stats struct {
        Base map[string]int
        Modifiers []string
    }
    
    func StatsCreate(stats BaseStats) Stats {
        s := Stats{
            Base: make(map[string]int),
        }
    
        //Iterate through the fields of a struct
        v := reflect.ValueOf(stats)
        typeOfS := v.Type()
    
        for i := 0; i< v.NumField(); i++ {
            val := v.Field(i).Interface().(int)
            s.Base[typeOfS.Field(i).Name] = val
        }
        return s
    }
    
    func (s Stats) GetBaseStat(id string) int {
        return s.Base[id]
    }
    
    
    func main() {
        m := StatsCreate(BaseStats{300, 300, 300, 300, 10, 10, 10})
    
        fmt.Println(m.GetBaseStat("Hp"))
    }
    
    
    

    【讨论】:

      【解决方案7】:

      使用reflect 包。首先,用reflect.TypeOf获取变量的类型,用reflect.NumField获取元素个数。要迭代获取结构体的字段值,必须反映变量并使用函数rg.Elem().Field(i)

      package main
      
      import (
          "fmt"
          "reflect"
      )
      
      type Gopher struct {
          Name  string
          Color string
          Year  int
      }
      
      func main() {
          g := Gopher{Name: "AAA", Color: "BBBB", Year: 2021}
      
          gtype := reflect.TypeOf(g)
      
          numFields := gtype.NumField()
      
          rg := reflect.ValueOf(&g)
      
          for i := 0; i < numFields; i++ {
              fmt.Println(rg.Elem().Field(i))
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-02-17
        • 1970-01-01
        • 2014-04-02
        • 1970-01-01
        • 1970-01-01
        • 2017-03-04
        • 2023-03-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多