Getting substruct field in Go

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

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()
}

https://play.golang.org/p/eZyTl8OSTZ

huangapple
  • 本文由 发表于 2017年3月29日 00:25:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/43074939.html
匿名

发表评论

匿名网友

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

确定