Iterate through the fields of a struct in Go

huangapple go评论110阅读模式
英文:

Iterate through the fields of a struct in Go

问题

基本上,我所知道的遍历struct字段值的唯一方法是这样的:

  1. type Example struct {
  2. a_number uint32
  3. a_string string
  4. }
  5. //...
  6. r := &Example{(2 << 31) - 1, "...."}
  7. for _, d := range []interface{}{r.a_number, r.a_string} {
  8. //对d进行一些操作
  9. }

我想知道是否有更好、更灵活的方法来实现[]interface{}{r.a_number, r.a_string},这样我就不需要逐个列出每个参数,或者是否有更好的方法来遍历一个struct

我尝试查看了reflect包,但我遇到了困难,因为我不确定一旦我获取了reflect.ValueOf(*r).Field(0)后该怎么做。

谢谢!

英文:

Basically, the only way (that I know of) to iterate through the values of the fields of a struct is like this:

  1. type Example struct {
  2. a_number uint32
  3. a_string string
  4. }
  5. //...
  6. r := &amp;Example{(2 &lt;&lt; 31) - 1, &quot;....&quot;}:
  7. for _, d:= range []interface{}{ r.a_number, r.a_string, } {
  8. //do something with the d
  9. }

I was wondering, if there's a better and more versatile way of achieving []interface{}{ r.a_number, r.a_string, }, so I don't need to list each parameter individually, or alternatively, is there a better way to loop through a struct?

I tried to look through the reflect package, but I hit a wall, because I'm not sure what to do once I retrieve reflect.ValueOf(*r).Field(0).

Thanks!

答案1

得分: 180

在使用Field(i)获取字段的reflect.Value之后,你可以通过调用Interface()方法从中获取一个接口值。该接口值表示字段的值。

由于Go语言中没有泛型,所以没有将字段的值转换为具体类型的函数。因此,没有带有签名GetValue() T的函数,其中T是字段的类型(当然,这取决于字段)。

在Go语言中,你可以最接近实现的是GetValue() interface{},而这正是reflect.Value.Interface()提供的功能。

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

  1. import (
  2. "fmt"
  3. "reflect"
  4. )
  5. func main() {
  6. x := struct{ Foo string; Bar int }{"foo", 2}
  7. v := reflect.ValueOf(x)
  8. values := make([]interface{}, v.NumField())
  9. for i := 0; i < v.NumField(); i++ {
  10. values[i] = v.Field(i).Interface()
  11. }
  12. fmt.Println(values)
  13. }
英文:

After you've retrieved the reflect.Value of the field by using Field(i) you can get a
interface value from it by calling Interface(). Said interface value then represents the
value of the field.

There is no function to convert the value of the field to a concrete type as there are,
as you may know, no generics in go. Thus, there is no function with the signature GetValue() T
with T being the type of that field (which changes of course, depending on the field).

The closest you can achieve in go is GetValue() interface{} and this is exactly what reflect.Value.Interface()
offers.

The following code illustrates how to get the values of each exported field in a struct
using reflection (play):

  1. import (
  2. &quot;fmt&quot;
  3. &quot;reflect&quot;
  4. )
  5. func main() {
  6. x := struct{Foo string; Bar int }{&quot;foo&quot;, 2}
  7. v := reflect.ValueOf(x)
  8. values := make([]interface{}, v.NumField())
  9. for i := 0; i &lt; v.NumField(); i++ {
  10. values[i] = v.Field(i).Interface()
  11. }
  12. fmt.Println(values)
  13. }

答案2

得分: 103

如果你想遍历结构体的字段和值,你可以参考以下的Go代码:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Student struct {
  7. Fname string
  8. Lname string
  9. City string
  10. Mobile int64
  11. }
  12. func main() {
  13. s := Student{"Chetan", "Kumar", "Bangalore", 7777777777}
  14. v := reflect.ValueOf(s)
  15. typeOfS := v.Type()
  16. for i := 0; i < v.NumField(); i++ {
  17. fmt.Printf("Field: %s\tValue: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface())
  18. }
  19. }

playground上运行。

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

英文:

If you want to Iterate through the Fields and Values of a struct then you can use the below Go code as a reference.

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. type Student struct {
  7. Fname string
  8. Lname string
  9. City string
  10. Mobile int64
  11. }
  12. func main() {
  13. s := Student{&quot;Chetan&quot;, &quot;Kumar&quot;, &quot;Bangalore&quot;, 7777777777}
  14. v := reflect.ValueOf(s)
  15. typeOfS := v.Type()
  16. for i := 0; i&lt; v.NumField(); i++ {
  17. fmt.Printf(&quot;Field: %s\tValue: %v\n&quot;, typeOfS.Field(i).Name, v.Field(i).Interface())
  18. }
  19. }

Run in playground

Note: If the Fields in your struct are not exported then the v.Field(i).Interface() will give panic panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

答案3

得分: 35

Go 1.17(2021年第三季度)应该通过提交009bfeaCL 281233添加一个新选项,修复问题42782

> ## reflect: 添加VisibleFields函数
>
> 在编写反射结构类型的代码时,通常需要知道完整的结构字段集,包括由于嵌入匿名成员而可用的字段,同时排除因为它们与另一个同名字段处于相同级别而被抹除的字段。
>
> 实现这个逻辑并不复杂,但有点微妙且容易出错。
>
> 这个CL在reflect包中添加了一个新的reflect.VisibleFields()函数,它返回适用于给定结构类型的完整字段集。

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

示例:

  1. type employeeDetails struct {
  2. id int16
  3. name string
  4. designation string
  5. }
  6. func structIterator() {
  7. fields := reflect.VisibleFields(reflect.TypeOf(struct{ employeeDetails }{}))
  8. for _, field := range fields {
  9. fmt.Printf("Key: %s\tType: %s\n", field.Name, field.Type)
  10. }
  11. }
英文:

Go 1.17 (Q3 2021) should add a new option, through commit 009bfea and CL 281233, fixing issue 42782.

> ## reflect: add VisibleFields function
>
> When writing code that reflects over a struct type, it's a common requirement to know the full set of struct fields, including fields available due to embedding of anonymous members while excluding fields that are erased because they're at the same level as another field with the same name.
>
> The logic to do this is not that complex, but it's a little subtle and easy to get wrong.
>
> This CL adds a new reflect.VisibleFields() function to the reflect package that returns the full set of effective fields that apply in a given struct type.

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

Example,

  1. type employeeDetails struct {
  2. id int16
  3. name string
  4. designation string
  5. }
  6. func structIterator() {
  7. fields := reflect.VisibleFields(reflect.TypeOf(struct{ employeeDetails }{}))
  8. for _, field := range fields {
  9. fmt.Printf(&quot;Key: %s\tType: %s\n&quot;, field.Name, field.Type)
  10. }
  11. }

答案4

得分: 7

也许有点晚了 :))) 但是还有另一种解决方案,你可以找到结构体的键和值,并对其进行迭代。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type person struct {
  7. firsName string
  8. lastName string
  9. iceCream []string
  10. }
  11. func main() {
  12. u := struct {
  13. myMap map[int]int
  14. mySlice []string
  15. myPerson person
  16. }{
  17. myMap: map[int]int{1: 10, 2: 20},
  18. mySlice: []string{"red", "green"},
  19. myPerson: person{
  20. firsName: "Esmaeil",
  21. lastName: "Abedi",
  22. iceCream: []string{"Vanilla", "chocolate"},
  23. },
  24. }
  25. v := reflect.ValueOf(u)
  26. for i := 0; i < v.NumField(); i++ {
  27. fmt.Println(v.Type().Field(i).Name)
  28. fmt.Println("\t", v.Field(i))
  29. }
  30. }

并且对于 v.Field(i) 不会出现 panic

英文:

Maybe too late :))) but there is another solution that you can find the key and value of structs and iterate over that

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. type person struct {
  7. firsName string
  8. lastName string
  9. iceCream []string
  10. }
  11. func main() {
  12. u := struct {
  13. myMap map[int]int
  14. mySlice []string
  15. myPerson person
  16. }{
  17. myMap: map[int]int{1: 10, 2: 20},
  18. mySlice: []string{&quot;red&quot;, &quot;green&quot;},
  19. myPerson: person{
  20. firsName: &quot;Esmaeil&quot;,
  21. lastName: &quot;Abedi&quot;,
  22. iceCream: []string{&quot;Vanilla&quot;, &quot;chocolate&quot;},
  23. },
  24. }
  25. v := reflect.ValueOf(u)
  26. for i := 0; i &lt; v.NumField(); i++ {
  27. fmt.Println(v.Type().Field(i).Name)
  28. fmt.Println(&quot;\t&quot;, v.Field(i))
  29. }
  30. }
  1. and there is no *panic* for v.Field(i)

答案5

得分: 0

使用以下代码:

  1. type x struct {
  2. Id int
  3. jsj int
  4. }
  5. func main() {
  6. x2 := x{jsj: 10, Id: 5}
  7. v := reflect.ValueOf(x2)
  8. for i := 0; i < v.NumField(); i++ {
  9. fmt.Println(v.Field(i))
  10. }
  11. }

输出:

  1. 10
  2. 5
英文:

use this:

  1. type x struct {
  2. Id int
  3. jsj int
  4. }
  5. func main() {
  6. x2 := x{jsj: 10, Id: 5}
  7. v := reflect.ValueOf(x2)
  8. for i := 0; i &lt; v.NumField(); i++ {
  9. fmt.Println(v.Field(i))
  10. }
  11. }

====>10

====>5

答案6

得分: -1

采用Chetan Kumar的解决方案,如果你需要应用到map[string]int类型的情况下:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type BaseStats struct {
  7. Hp int
  8. HpMax int
  9. Mp int
  10. MpMax int
  11. Strength int
  12. Speed int
  13. Intelligence int
  14. }
  15. type Stats struct {
  16. Base map[string]int
  17. Modifiers []string
  18. }
  19. func StatsCreate(stats BaseStats) Stats {
  20. s := Stats{
  21. Base: make(map[string]int),
  22. }
  23. // 遍历结构体的字段
  24. v := reflect.ValueOf(stats)
  25. typeOfS := v.Type()
  26. for i := 0; i < v.NumField(); i++ {
  27. val := v.Field(i).Interface().(int)
  28. s.Base[typeOfS.Field(i).Name] = val
  29. }
  30. return s
  31. }
  32. func (s Stats) GetBaseStat(id string) int {
  33. return s.Base[id]
  34. }
  35. func main() {
  36. m := StatsCreate(BaseStats{300, 300, 300, 300, 10, 10, 10})
  37. fmt.Println(m.GetBaseStat("Hp"))
  38. }
英文:

Taking Chetan Kumar solution and in case you need to apply to a map[string]int

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. type BaseStats struct {
  7. Hp int
  8. HpMax int
  9. Mp int
  10. MpMax int
  11. Strength int
  12. Speed int
  13. Intelligence int
  14. }
  15. type Stats struct {
  16. Base map[string]int
  17. Modifiers []string
  18. }
  19. func StatsCreate(stats BaseStats) Stats {
  20. s := Stats{
  21. Base: make(map[string]int),
  22. }
  23. //Iterate through the fields of a struct
  24. v := reflect.ValueOf(stats)
  25. typeOfS := v.Type()
  26. for i := 0; i&lt; v.NumField(); i++ {
  27. val := v.Field(i).Interface().(int)
  28. s.Base[typeOfS.Field(i).Name] = val
  29. }
  30. return s
  31. }
  32. func (s Stats) GetBaseStat(id string) int {
  33. return s.Base[id]
  34. }
  35. func main() {
  36. m := StatsCreate(BaseStats{300, 300, 300, 300, 10, 10, 10})
  37. fmt.Println(m.GetBaseStat(&quot;Hp&quot;))
  38. }

答案7

得分: -1

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

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Gopher struct {
  7. Name string
  8. Color string
  9. Year int
  10. }
  11. func main() {
  12. g := Gopher{Name: "AAA", Color: "BBBB", Year: 2021}
  13. gtype := reflect.TypeOf(g)
  14. numFields := gtype.NumField()
  15. rg := reflect.ValueOf(&g)
  16. for i := 0; i < numFields; i++ {
  17. fmt.Println(rg.Elem().Field(i))
  18. }
  19. }
英文:

Use reflect package. First, get the type of variable with reflect.TypeOf and get numbers of elements with reflect.NumField.To obtain the values of the fields iteratively of a structure must reflect the variable and use the function rg.Elem().Field(i)

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. type Gopher struct {
  7. Name string
  8. Color string
  9. Year int
  10. }
  11. func main() {
  12. g := Gopher{Name: &quot;AAA&quot;, Color: &quot;BBBB&quot;, Year: 2021}
  13. gtype := reflect.TypeOf(g)
  14. numFields := gtype.NumField()
  15. rg := reflect.ValueOf(&amp;g)
  16. for i := 0; i &lt; numFields; i++ {
  17. fmt.Println(rg.Elem().Field(i))
  18. }
  19. }

答案8

得分: -2

在Go语言中,你可以使用reflect包来遍历结构体的字段。reflect包允许你在运行时检查值的属性,包括它们的类型和值。下面是一个遍历结构体字段的示例:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Movie struct {
  7. Name string
  8. Year int
  9. }
  10. func main() {
  11. p := Movie{"The Dark Knight", 2008}
  12. val := reflect.ValueOf(p)
  13. typ := val.Type()
  14. for i := 0; i < val.NumField(); i++ {
  15. field := val.Field(i)
  16. fieldType := typ.Field(i)
  17. fmt.Printf("字段名:%s,字段值:%v\n", fieldType.Name, field.Interface())
  18. }
  19. }

输出结果:

  1. 字段名:Name,字段值:The Dark Knight
  2. 字段名:Year,字段值:2008

你可以在Go Playground上运行这段代码。

英文:

In Go, you can use the reflect package to iterate through the fields of a struct. The reflect package allows you to inspect the properties of values at runtime, including their type and value. Here's an example of how to iterate through the fields of a struct:

Go Playground

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;reflect&quot;
  5. )
  6. type Movie struct {
  7. Name string
  8. Year int
  9. }
  10. func main() {
  11. p := Movie{&quot;The Dark Knight&quot;, 2008}
  12. val := reflect.ValueOf(p)
  13. typ := val.Type()
  14. for i := 0; i &lt; val.NumField(); i++ {
  15. field := val.Field(i)
  16. fieldType := typ.Field(i)
  17. fmt.Printf(&quot;Field Name: %s, Field Value: %v\n&quot;, fieldType.Name, field.Interface())
  18. }
  19. }

Output:

  1. Field Name: Name, Field Value: The Dark Knight
  2. Field Name: Age, Field Value: 2008

huangapple
  • 本文由 发表于 2013年9月21日 05:39:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/18926303.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定