Iterate through the fields of a struct in Go

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

Iterate through the fields of a struct in Go

问题

基本上,我所知道的遍历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} {
  //对d进行一些操作
}

我想知道是否有更好、更灵活的方法来实现[]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:

type Example struct {
    a_number uint32
    a_string string
}

//...

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

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链接):

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)
}
英文:

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

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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

	v := reflect.ValueOf(x)

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

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

	fmt.Println(values)
}

答案2

得分: 103

如果你想遍历结构体的字段和值,你可以参考以下的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.

英文:

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

package main

import (
    &quot;fmt&quot;
    &quot;reflect&quot;
)

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

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

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

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()函数,它返回适用于给定结构类型的完整字段集。

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)
	}
}
英文:

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.

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

Example,

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

答案4

得分: 7

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

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

并且对于 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

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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{&quot;red&quot;, &quot;green&quot;},
		myPerson: person{
			firsName: &quot;Esmaeil&quot;,
			lastName: &quot;Abedi&quot;,
			iceCream: []string{&quot;Vanilla&quot;, &quot;chocolate&quot;},
		},
	}
	v := reflect.ValueOf(u)
	for i := 0; i &lt; v.NumField(); i++ {
		fmt.Println(v.Type().Field(i).Name)
		fmt.Println(&quot;\t&quot;, v.Field(i))
	}
}
and there is no *panic* for v.Field(i)

答案5

得分: 0

使用以下代码:

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
英文:

use this:

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

====>10

====>5

答案6

得分: -1

采用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),
	}

	// 遍历结构体的字段
	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"))
}
英文:

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

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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&lt; 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(&quot;Hp&quot;))
}


答案7

得分: -1

使用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))
	}
}
英文:

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)

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type Gopher struct {
	Name  string
	Color string
	Year  int
}

func main() {
	g := Gopher{Name: &quot;AAA&quot;, Color: &quot;BBBB&quot;, Year: 2021}

	gtype := reflect.TypeOf(g)

	numFields := gtype.NumField()

	rg := reflect.ValueOf(&amp;g)

	for i := 0; i &lt; numFields; i++ {
		fmt.Println(rg.Elem().Field(i))
	}
}

答案8

得分: -2

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

package main

import (
	"fmt"
	"reflect"
)

type Movie struct {
	Name string
	Year int
}

func main() {
	p := Movie{"The Dark Knight", 2008}

	val := reflect.ValueOf(p)
	typ := val.Type()

	for i := 0; i < val.NumField(); i++ {
		field := val.Field(i)
		fieldType := typ.Field(i)

		fmt.Printf("字段名:%s,字段值:%v\n", fieldType.Name, field.Interface())
	}
}

输出结果:

字段名:Name,字段值:The Dark Knight
字段名: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

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type Movie struct {
	Name string
	Year int
}

func main() {
	p := Movie{&quot;The Dark Knight&quot;, 2008}

	val := reflect.ValueOf(p)
	typ := val.Type()

	for i := 0; i &lt; val.NumField(); i++ {
		field := val.Field(i)
		fieldType := typ.Field(i)

		fmt.Printf(&quot;Field Name: %s, Field Value: %v\n&quot;, fieldType.Name, field.Interface())
	}
}

Output:

Field Name: Name, Field Value: The Dark Knight
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:

确定