【问题标题】:golang recurisive reflectiongolang 递归反射
【发布时间】:2017-12-03 23:05:45
【问题描述】:

我试图递归地反映一个结构,打印出每个字段的类型。在字段是结构切片的情况下,我希望能够 识别数组中保存的类型,然后反映该类型。

这是一些示例代码

package main

import (
    "log"
    "reflect"
)

type child struct {
    Name *string
    Age  int
}

type Parent struct {
    Name     string
    Surname  *string
    Children []*child
    PetNames []string
}

func main() {

    typ := reflect.TypeOf(Parent{})
    log.Printf("This is a : %s", typ.Kind())

    for i := 0; i < typ.NumField(); i++ {
        p := typ.Field(i)
        if !p.Anonymous {
            switch p.Type.Kind() {
            case reflect.Ptr:
                log.Printf("Ptr: %s is a type %s", p.Name, p.Type)
            case reflect.Slice:
                log.Printf("Slice: %s is a type %s", p.Name, p.Type)
                subtyp := p.Type.Elem()
                if subtyp.Kind() == reflect.Ptr {
                    subtyp = subtyp.Elem()
                }
                log.Printf("\tDereferenced Type%s", subtyp)
            default:
                log.Printf("Default: %s is a type %s", p.Name, p.Type)
            }
        }
    }

}

输出如下所示:

This is a : struct
Default: Name is a type string
Ptr: Surname is a type *string
Slice: Children is a type []*main.child
    Dereferenced Type main.child
Slice: PetNames is a type []string
    Dereferenced Type string

当我确定一个字段类型是一个指针切片时,我可以通过调用 subtype.Elem() 来推断类型。

输出是'main.child'

如果我然后尝试使用来反映孩子

subSubType := reflect.TypeOf(subtyp)
log.Printf("%+v", subSubType) 

我得到以下信息:

 *reflect.rtype

如何使用反射 API 来迭代子结构的字段?

【问题讨论】:

  • 不要调用reflect.TypeOf(subtyp)subtyp 已经是reflect.Type 类型。
  • play.golang.org/p/Th0uGACjwH 这只是一个例子,我不喜欢我处理字段名称的方式,但是这里已经很晚了,所以希望这会为您指明正确的方向。

标签: go reflection


【解决方案1】:

这是一种方法。

func printType(prefix string, t reflect.Type, visited map[reflect.Type]bool) {

    // Print the name of this type with opening ( for description.
    fmt.Printf("%s (", t)

    // Traverse elements, adding to description as we go.
elems:
    for {
        switch t.Kind() {
        case reflect.Ptr:
            fmt.Print("ptr to ")
        case reflect.Slice:
            fmt.Print("slice of ")
        case reflect.Array:
            fmt.Printf("array with %d elements of ", t.Len())
        default:
            break elems
        }
        t = t.Elem()
    }

    // Print the kind of the type and the closing ) of the description.
    // In the case of a struct, we print the names of the fields and recurse.
    switch t.Kind() {
    case reflect.Struct:
        fmt.Printf("struct with %d fields)\n", t.NumField())
        if visited[t] {
            // Don't blow up on recursive type definition.
            break
        }
        visited[t] = true
        prefix += "    "
        for i := 0; i < t.NumField(); i++ {
            f := t.Field(i)
            fmt.Print(prefix, f.Name, " ")
            printType(prefix, f.Type, visited)
        }
    default:
        fmt.Printf("%s)\n", t.Kind())
    }
}

func main() {
    printType("", reflect.TypeOf(Parent{}), make(map[reflect.Type]bool))
}

给定以下类型的 Parent{} 的输出:

type child struct {
    Name *string
    Age  int
}

type Parent struct {
    Name     string
    Surname  *string
    Children []*child
    PetNames []string
    Parents  [2]*Parent
    child
}

是:

main.Parent (struct with 6 fields)
    Name string (string)
    Surname *string (ptr to string)
    Children []*main.child (slice of ptr to struct with 2 fields)
        Name *string (ptr to string)
        Age int (int)
    PetNames []string (slice of string)
    Parents [2]*main.Parent (array with 2 elements of ptr to struct with 6 fields)
    child main.child (struct with 2 fields)

playground example

【讨论】:

  • 感谢这项工作。如果我想传递一个带有值的结构,是否也可以提取每个字段的值?即parent := Parent{Name: "Joe", Children: []*child{&child{Name: "Bobby", Age: 4,},},} 然后输出将打印 "Name *string (ptr to string) (鲍比)”?
  • 上面的代码适用于问题中提出的类型。值的代码将非常相似。使用 fmt.Sprintf("%v", v) 打印值。
猜你喜欢
  • 2022-01-24
  • 1970-01-01
  • 2011-02-11
  • 2013-01-18
  • 1970-01-01
  • 2015-11-18
  • 2018-05-21
  • 2013-06-27
  • 1970-01-01
相关资源
最近更新 更多