英文:
Getting substruct field in Go
问题
我正在尝试使用反射从struct值中获取字段。
package main
import (
	"fmt"
	"reflect"
)
type Vertex struct {
	X         string
	Y         string
	SubVertex SubVertex
}
type SubVertex struct {
	Z string
}
func get_field(v Vertex, field string) string {
	r := reflect.ValueOf(v)
	f := reflect.Indirect(r).FieldByName(field)
	return f.String()
}
func main() {
	v := Vertex{"a", "b", SubVertex{"c"}}
	fmt.Println(get_field(v, "X"))
	fmt.Println(get_field(v, "Y"))
	fmt.Println(get_field(v, "Z"))  // Invalid Value
}
在第三个案例中,当我尝试获取Z字段的值时,我得到了Invalid Value。如果SubVertex是一个匿名字段,这将起作用,但我需要使用一个命名字段。
我该如何使其工作?
英文:
I am trying to get fields from a struct value using reflection.
package main
import (
	"fmt"
	"reflect"
)
type Vertex struct {
	X         string
	Y         string
	SubVertex SubVertex
}
type SubVertex struct {
	Z string
}
func get_field(v Vertex, field string) string {
	r := reflect.ValueOf(v)
	f := reflect.Indirect(r).FieldByName(field)
	return f.String()
}
func main() {
	v := Vertex{"a", "b", SubVertex{"c"}}
	fmt.Println(get_field(v, "X"))
	fmt.Println(get_field(v, "Y"))
	fmt.Println(get_field(v, "Z"))  // Invalid Value
}
I get Invalid Value in the third case, when I try to get the value of the Z field. If SubVertex were an anonymous field, this would work, but I need to use a named field.
How do I make this work?
答案1
得分: 4
在这种情况下,您必须像正常访问值一样使用reflect包。所以
v.X // a
v.Y // b
v.SubVertex.Z // c
变成了
r := reflect.ValueOf(v)
x := reflect.Indirect(r).FieldByName("X")
x.String() // a
...
z := reflect.Indirect(r).FieldByName("SubVertex").FieldByName("Z")
z.String() // c
请注意,FieldByName() 在 Value 上调用并返回一个 Value,因此它的工作方式与正常访问相同。还请注意,根据文档:
> Indirect返回v指向的值。如果v是nil指针,则Indirect返回零值。如果v不是指针,则Indirect返回v。
因此,对Indirect()的调用将是一个无操作,但如果您决定在将来给它一个指针,它将保护它免受崩溃的影响。
至于您的函数,可以这样实现:
func get_field(v Vertex, field string) string {
    r := reflect.ValueOf(v)
    if field == "Z" {
        f := reflect.Indirect(r).FieldByName("SubVertex").FieldByName(field)
        return f.String()
    }
    f := reflect.Indirect(r).FieldByName(field)
    return f.String()
}
链接:https://play.golang.org/p/eZyTl8OSTZ
英文:
In this case, you have to use the reflect package in the same manner as you would accessing the values normally. So
v.X // a
v.Y // b
v.SubVertex.Z // c
becomes
r := reflect.ValueOf(v)
x := reflect.Indirect(r).FieldByName("X")
x.String() // a
...
z := reflect.Indirect(r).FieldByName("SubVertex").FieldByName("Z")
z.String() // c
Note that FieldByName() is called on a Value and returns a Value, so it works much the same as just accessing it regularly. Also note that as per the documentation:
> Indirect returns the value that v points to. If v is a nil pointer, Indirect returns a zero Value. If v is not a pointer, Indirect returns v.
So the call to Indirect() would be a No-op, but would protect it from having a meltdown if you decided to give it a pointer in the future.
As for your function, this would work
func get_field(v Vertex, field string) string {
    r := reflect.ValueOf(v)
    if field == "Z" {
        f := reflect.Indirect(r).FieldByName("SubVertex").FieldByName(field)
        return f.String()
    }
    f := reflect.Indirect(r).FieldByName(field)
    return f.String()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论