基本上,(我所知道的)迭代 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)
后该怎么做。
谢谢!
使用 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)
}
如果要遍历结构的字段和值,则可以使用下面的 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.
Go 1.17(2021 年第三季度)应该通过 commit 009bfea 和 CL 281233 添加一个新选项,修复 issue 42782。
反射:添加 VisibleFields 功能在编写反映结构类型的代码时,通常需要了解完整的结构字段集,包括由于嵌入匿名成员而可用的字段,同时排除因为它们相同而被删除的字段级别作为另一个具有相同名称的字段。这样做的逻辑并没有那么复杂,但它有点微妙并且容易出错。此 CL 向反射包添加了一个新的 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.VisibleFields
。我试图编辑,但似乎队列已满。
也许为时已晚:))) 但是还有另一种解决方案,您可以找到结构的键和值并对其进行迭代
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)
用这个:
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
采取 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"))
}
使用 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))
}
}
reflect.Value.Type()
,是的。但请注意,类型不是 go 中的一等公民,因此您只能使用reflect
实例化该类型的新值。v.Field(i).Interface()
如果您尝试访问未导出的私有字段,则会出现混乱。请小心:)v.Field(i).CanInterface()
可以避免在未导出字段的情况下出现恐慌。