在 Go 中迭代结构体的字段

问题描述 投票:0回答:8

基本上,(据我所知)迭代

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
8个回答
184
投票

使用

reflect.Value
检索字段的
Field(i)
后,您可以获得 通过调用
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)
}

109
投票

如果你想迭代结构体的字段和值,那么你可以使用下面的 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.


37
投票

Go 1.17(2021 年第 3 季度)应通过 commit 009bfeaCL 281233 添加新选项,修复 issue 42782

reflect:添加VisibleFields功能

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

执行此操作的逻辑并不复杂,但有点微妙且容易出错。

此 CL 在

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

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(employeeDetails{}))
    for _, field := range fields {
        fmt.Printf("Key: %s\tType: %s\n", field.Name, field.Type)
    }
}

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))
    }
}
and there is no *panic* for v.Field(i)

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


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

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



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

-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("Field Name: %s, Field Value: %v\n", fieldType.Name, field.Interface())
    }
}

输出:

Field Name: Name, Field Value: The Dark Knight
Field Name: Age, Field Value: 2008
© www.soinside.com 2019 - 2024. All rights reserved.