在 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, } {
//do something with the d
}

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

我试图通过 reflect包看,但我碰壁了,因为我不知道一旦我检索 reflect.ValueOf(*r).Field(0)做什么。

谢谢!

225674 次浏览

使用 Field(i)检索字段的 reflect.Value之后,可以获得一个 接口值,然后该接口值表示 字段的值。

没有将字段值转换为具体类型的函数, 你可能知道,没有泛型去。因此,没有功能与签名 GetValue() T T是该字段的类型(当然,这会根据字段的不同而改变)。

最接近你可以达到的是 GetValue() interface{},这正是什么 reflect.Value.Interface() 出价。

下面的代码演示如何获取结构中每个导出字段的值 使用反射(玩吧) :

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 代码作为参考。

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

运行 游乐场

注意: 如果你的结构中的字段没有导出,那么 v.Field(i).Interface()会给出紧急 panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

采取 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"))
}




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

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)

去1.17(Q32021)应该增加一个新的选项,通过 承认错误CL 281233,固定 第42782期

反射: 添加 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)
}
}

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

使用:

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

要循环访问结构,可以首先使用 将它附加到 Slice。

就像这个例子:

去游乐场链接

package main


import (
"fmt"
)


type Movies struct {
Name string
Year int
}


func main() {


var s []Movies


s = append(s, Movies{Name: "The Dark Knight", Year: 2008})


for _, f := range s {
fmt.Printf("Name: %s\nYear: %d\n", f.Name, f.Year)


}
}

产出:

Name: The Dark Knight
Year: 2008