在Go语言中遍历结构体的字段

pxy2qtax  于 2022-12-20  发布在  Go
关注(0)|答案(8)|浏览(236)

基本上,迭代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)后该怎么做。
谢谢!

kulphzqa

kulphzqa1#

在使用Field(i)获取字段的reflect.Value之后,可以通过调用Interface()reflect.Value获取一个接口值,该接口值表示字段的值。
没有函数可以将字段的值转换为具体的类型,正如你所知,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)
}
vvppvyoh

vvppvyoh2#

如果你想迭代一个结构体的字段和值,你可以参考下面的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.

ni65a41a

ni65a41a3#

Go 1.17(2021年第三季度)应该会增加一个新的选项,通过commit 009bfeaCL 281233,修复issue 42782

反映:添加可见字段函数

当编写在结构类型上反射的代码时,通常需要知道结构字段的完整集合,包括由于嵌入匿名成员而可用的字段,同时排除由于与另一个同名字段处于同一级别而被擦除的字段。
这样做的逻辑并不复杂,但有点微妙,很容易出错。
此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)
    }
}
6fe3ivhb

6fe3ivhb4#

也许太晚了:)))但是还有另一种解决方案,你可以找到结构体的键和值,然后迭代它们

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

piok6c0g5#

使用此:

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

4uqofj5v

4uqofj5v6#

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

lvjbypge7#

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

9nvpjoqh8#

要迭代Struct,您可以先将其追加到Slice。
就像这个例子:
Go Playground link

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

相关问题