Go 反映字段索引 - 单个索引与切片

Go reflect field index - single index vs. slice

reflect.StructField 有一个输入 []intIndex 字段。关于这个的文档有点混乱:

    Index     []int     // index sequence for Type.FieldByIndex

当然 Type.FieldByIndex 也如预期的那样效仿,对其行为的解释更加清晰:

    // FieldByIndex returns the nested field corresponding
    // to the index sequence.  It is equivalent to calling Field
    // successively for each index i.
    // It panics if the type's Kind is not Struct.
    FieldByIndex(index []int) StructField

但是,还有Type.Field():

    // Field returns a struct type's i'th field.
    // It panics if the type's Kind is not Struct.
    // It panics if i is not in the range [0, NumField()).
    Field(i int) StructFiel

所以他们各自的行为非常清楚。

我的问题:reflect.StructFieldIndexlen(field.Index) > 1 究竟针对哪些字段/什么情况?这是否支持枚举嵌入式字段(可通过父级中的匿名字段访问)?在其他情况下会发生吗? (即假设 !field.Anonymous 是否安全,那么我们可以只使用 field.Index[0] 作为 Field(i int) 的参数?)

它可以递归地引用嵌入或非嵌入结构中的字段:

type Foo struct {
    Bar string
}

type Baz struct {
    Zoo Foo
}

func main() {

    b := Baz{Zoo:Foo{"foo"}}
    v := reflect.ValueOf(b)

    fmt.Println(v.FieldByIndex([]int{0})) //output: <main.Foo Value>

    fmt.Println(v.FieldByIndex([]int{0, 0})) //output: foo

}

这是一个例子。为了回答这个问题,我深入研究了反射测试。

package main

import (
    "fmt"
    "reflect"
)

type (
    Bar struct {
        Val string
    }

    Foo struct {
        Bar
    }
)

func main() {
    t := reflect.TypeOf(Foo{})
    f, _ := t.FieldByName("Val")
    fmt.Println(f.Index)         // [0 0]
}

所以我一直在寻找这个问题的答案,但我真的找不到任何东西。为了解释为什么上面的答案不尽如人意,我举个例子:

package main

import (
    "fmt"
    "reflect"
)

type (
    A struct {
        W int
        X int
    }
    B struct {
        Y int
        A A
    }
    C struct {
        B B
        Z int
    }
)

func main() {
    b := B{1, A{2, 3}}
    c := C{b, 4}

    bt := reflect.TypeOf(b)
    ct := reflect.TypeOf(c)

    ba := bt.FieldByIndex([]int{1, 0})
    ca := ct.FieldByIndex([]int{0, 1, 0})

    fmt.Println("B > A = ", ba.Index)
    fmt.Println("C > B > A = ", ca.Index)
}

输出为:

B > A = [0]
C > B > A = [0]

因此,根据文档中对 StructField.Index 的描述(

Index []int // index sequence for Type.FieldByIndex

) 人们会假设输出会以某种方式对应于通过 FieldByIndex 方法检索相同的字段,并且由于它是为嵌套字段设计的,所以上面的输出可能会令人困惑。如果 Index 始终是长度为 1 的 []int,为什么还要使用数组?如果它仅与其直接父级相关,为什么它不存储单个 int?

答案可能比我们(我们这些感到困惑的人)预期的要简单。 Index 值必须经常用作 FieldByIndex 的参数,因此为了方便起见,它存储在数组中。