【问题标题】:Go reflect field index - single index vs. slice去反射字段索引 - 单索引与切片
【发布时间】:2022-03-02 01:13:20
【问题描述】:

reflect.StructField 有一个Index 字段,其类型为[]int。这方面的文档有点令人困惑:

    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.StructField 究竟在哪些字段/什么情况下会有Indexlen(field.Index) > 1?这是否支持枚举嵌入字段(可通过父项中的匿名字段访问)?在其他情况下会发生吗? (即,假设!field.Anonymous 是否安全,那么我们可以只使用field.Index[0] 作为Field(i int) 的参数?)

【问题讨论】:

  • ...!field.Anonymous 或只能通过匿名/嵌入式字段访问的字段,我想我应该说。一开始没有考虑嵌入结构的非匿名字段。

标签: reflection go


【解决方案1】:

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

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

}

【讨论】:

  • 谢谢...不过,正如我所说,文档很清楚,我理解 FieldByIndex() 的作用。我的问题是关于 reflect.StructFieldIndex 字段 - 请参阅问题的最后两句。
  • @BadZen 我刚刚给你举了一个例子,说明它如何用于非嵌入式结构。但是使用单个索引应该没问题。
【解决方案2】:

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

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 作者的想法,现在已经过去了十年。无论如何我都会接受这个,因为我认为这是对这个问题最有价值的答案——或者这个问题很可能永远不会有一个人停下来。
【解决方案3】:

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

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]
}

【讨论】:

    猜你喜欢
    • 2018-07-29
    • 2010-09-17
    • 2019-10-28
    • 2013-08-09
    • 1970-01-01
    • 2011-08-28
    • 2021-12-31
    • 2021-08-25
    • 1970-01-01
    相关资源
    最近更新 更多