迭代Go中结构的字段

omn*_*nse 81 go go-reflect

基本上,迭代a字段值的唯一方法(我知道)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
}
Run Code Online (Sandbox Code Playgroud)

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

我试图透过reflect包裹看,但是我碰了一堵墙,因为我不知道一旦取回就该做什么reflect.ValueOf(*r).Field(0).

谢谢!

nem*_*emo 98

使用后检索reflect.Value字段后,Field(i)可以通过调用从中获取接口值Interface().然后,所述界面值表示该字段的值.

没有功能可以将字段的值转换为具体类型,因为您可能知道,go中没有泛型.因此,不存在与签名没有功能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)
}
Run Code Online (Sandbox Code Playgroud)

  • 是的因为go不需要泛型.咳嗽,咳嗽:-)有没有办法获得该领域的类型? (14认同)
  • 使用`v.Field(i).CanInterface()`可以避免在未导出字段的情况下发生混乱. (8认同)
  • `v.Field(i).Interface()`如果你试图访问非导出的私有字段,就会发生恐慌.小心:) (4认同)
  • 如何获取字段名称? (4认同)

小智 60

如果您想遍历结构的字段和值,则可以使用以下 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())
    }
}
Run Code Online (Sandbox Code Playgroud)

在操场上奔跑

注意:如果您的结构中的字段未导出,v.Field(i).Interface()则会导致恐慌panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.


Esm*_*edi 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))
    }
}
Run Code Online (Sandbox Code Playgroud)
and there is no *panic* for v.Field(i)
Run Code Online (Sandbox Code Playgroud)

  • 您好,欢迎来到 Stack Overflow!请参加[游览](https://stackoverflow.com/tour)。感谢您提供答案(问题的年龄并不重要)。您能多解释一下您的答案如何解决问题吗? (3认同)

Von*_*onC 6

Go 1.17(2021 年第三季度)应该添加一个新选项,通过提交 009bfea和CL 281233修复问题 42782。

反映:添加 VisibleFields 功能

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

这样做的逻辑并不复杂,但它有点微妙且容易出错。

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

fields := reflect.VisibleFields(typ)
for j, field := range fields {
    ...
}
Run Code Online (Sandbox Code Playgroud)

例子,

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)
    }
}
Run Code Online (Sandbox Code Playgroud)