Go reflect field index – 单个索引 vs. 切片

huangapple go评论76阅读模式
英文:

Go reflect field index - single index vs. slice

问题

reflect.StructFieldIndex字段的类型是[]int。关于这一点的文档有些令人困惑:

    Index     []int     // 用于Type.FieldByIndex的索引序列

当然,Type.FieldByIndex也按预期的方式遵循相同的模式,其行为的解释稍微清晰一些:

    // FieldByIndex返回与索引序列对应的嵌套字段。
    // 它等效于依次为每个索引i调用Field。
    // 如果类型的Kind不是Struct,则会引发panic。
    FieldByIndex(index []int) StructField

但是,还有Type.Field()

    // Field返回结构类型的第i个字段。
    // 如果类型的Kind不是Struct,则会引发panic。
    // 如果i不在范围[0, NumField())内,则会引发panic。
    Field(i int) StructField

因此,它们的行为分别非常清晰。

我的问题是:在哪些字段/什么情况下,reflect.StructFieldIndex会具有len(field.Index) > 1?这是为了支持枚举嵌入字段(通过父字段中的匿名字段可访问)吗?还会发生在其他情况下吗?(即,可以安全地假设如果!field.Anonymous,那么我们可以将field.Index[0]作为Field(i int)的参数使用吗?)

英文:

reflect.StructField has an Index field that is typed []int. The documentation on this is slightly confounding:

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

And of course Type.FieldByIndex follows suit as expected, with the somewhat clearer explanation of its behavior:

    // 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

But, there is also 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

So the behavior of those respectively is very clear.

My question: Exactly for which fields / what circumstances will a reflect.StructField have an Index with len(field.Index) > 1? Is this there to support enumerating embedded fields (reachable through an anonymous field in the parent)? Does it happen in other cases? (ie. is it safe to assume if !field.Anonymous, then we can just use field.Index[0] as an argument to Field(i int)?)

答案1

得分: 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})) //输出: <main.Foo Value>

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

}
英文:

It can refer to fields in embedded or non embedded structs, recursively:

type Foo struct {
	Bar string
}

type Baz struct {
	Zoo Foo
}

func main() {

	b := Baz{Zoo:Foo{&quot;foo&quot;}}
	v := reflect.ValueOf(b)

	fmt.Println(v.FieldByIndex([]int{0})) //output: &lt;main.Foo Value&gt;

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

}

答案2

得分: 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]
}

这段代码中,我们定义了两个结构体类型:Bar和Foo。Foo嵌入了Bar。在main函数中,我们使用反射获取了Foo类型的反射对象t,并通过FieldByName方法获取了名为"Val"的字段的反射对象f。最后,我们打印了字段f的索引值,结果为[0 0]。

英文:

Here is an example. To answer this question, I've dig into reflect tests.

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type (
	Bar struct {
		Val string
	}

	Foo struct {
		Bar
	}
)

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

答案3

得分: 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 // index sequence for Type.FieldByIndex

),人们可能会认为输出结果在某种程度上与通过FieldByIndex方法检索相同字段相对应,而且由于该方法是为嵌套字段设计的,上面的输出结果可能会令人困惑。如果Index始终是长度为1的[]int,为什么要使用数组呢?如果它只与其直接父级有关,为什么不存储一个单独的整数?

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

英文:

So I was looking for the answer to this question, and I really haven't been able to find anything. To explain why the answer above is not satisfactory, I have an example:

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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(&quot;B &gt; A = &quot;, ba.Index)
	fmt.Println(&quot;C &gt; B &gt; A = &quot;, ca.Index)
}

The output is:

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

So with the description of StructField.Index as is given in the docs (
> Index []int // index sequence for Type.FieldByIndex

) one would assume the output would correspond in some way to retrieving the same field via the FieldByIndex method, and since that is designed for nested fields, the output above might be confusing. If the Index is always an []int of length 1, why even use an array? Why does it not store a single int if it is only in relation to its immediate parent?

The answer is probably simpler than we (those of us who found this confusing) expected. The Index value must often be used as an argument for FieldByIndex, and so it is stored in an array simply for convenience.

huangapple
  • 本文由 发表于 2015年6月1日 23:02:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/30576874.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定