Go反射字段索引 - 单个索引 vs. 切片

4

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.StructFieldIndex 会具有 len(field.Index) > 1?这是为了支持枚举嵌入字段(通过父级中的匿名字段可访问)吗?是否会发生其他情况?(即,如果 !field.Anonymous,那么我们可以安全地假设只需使用 field.Index[0] 作为 Field(i int) 的参数吗?)


...!field.Anonymous 或者是只能通过匿名/嵌入字段到达的字段,我想我应该这样说。起初没有考虑到嵌入结构的非匿名字段。 - BadZen
3个回答

5
它可以递归地引用嵌入或非嵌入结构体中的字段:
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

}

1
谢谢...就像我说的,文档很清楚,我明白FieldByIndex()的作用。我的问题是关于reflect.StructFieldIndex字段——请看问题的最后两句话。 - BadZen
@BadZen 我刚刚给了你一个非嵌入式结构体的使用示例。但是你只使用单个索引也应该没问题。 - Not_a_Golfer

1
这是一个例子。为了回答这个问题,我深入研究了反射测试。
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]
}

1

我在寻找这个问题的答案,但是我还没有找到什么有用的东西。为了解释上面给出的答案不够满意的原因,我举了一个例子:

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 //用于Type.FieldByIndex的索引序列),人们会认为输出结果与通过FieldByIndex方法检索相同字段有某种联系,由于该方法是针对嵌套字段设计的,因此上面的输出可能会令人困惑。如果Index始终是长度为1的[]int,为什么要使用数组?如果它只与其直接父级相关,为什么不存储单个int?
答案可能比我们(那些发现这很困惑的人)预期的简单。Index值经常用作FieldByIndex的参数,因此它仅出于方便而存储在数组中。

这正是我最初提出问题的原因。回想起来(7年多的Go开发经验,哇!),我认为你的解释是正确的。但是我无法真正理解Go作者的想法,现在已经过去十年了。尽管如此,我仍然会接受这个答案,因为我认为这是这个问题中最有价值的答案 - 或者说这个问题可能会得到的最有价值的答案,除非其中一个作者路过。 - BadZen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接