【问题标题】:Golang dynamic variable referenceGolang 动态变量引用
【发布时间】:2016-12-20 10:42:37
【问题描述】:

在 Go 中,我想做这样的事情。我有一个包含许多结构的大对象(使用 Google 的 protobuf)。这是一个人为的例子:

person.name = "testing"
person.address.street = "123 test st"
person.address.city = "tester"
person.address.zip = 90210
person.billing.address.same = true

我希望能够动态引用事物。例如:

key := "person.address.zip"
fmt.Println("the value of key: " + key) // would like to get 90210
key := "person.address.city"
fmt.Println("the value of key: " + key) // would like to get "tester"

这在 Go 中可能吗?如果是这样,我该怎么做?本质上,我正在创建一个仅包含对象子集的报告,并且我希望能够创建一个映射文件,用户可以在其中将键/值映射在一起,并且我的程序将输出该值。我有这个在 python 中工作,但想尝试使用 Go :)

【问题讨论】:

  • 是的,有可能。它为您提供包反射。反射的缺点通常是工作量大。另一方面 - 没有反射就无法编写像 html/template 或 text/template 这样完美的包,其中的写作风格类似于解释语言(如 python),尽管 go 已编译。
  • 而且反射很慢,应该是最后的手段。
  • 如果你真的需要这样的东西,你应该使用(嵌套)映射,而不是结构值的层次结构。
  • map[string]interface{} 这样的嵌套地图是你的朋友

标签: variables dynamic go reflection go-reflect


【解决方案1】:

您可以使用来自reflect 包的func (v Value) FieldByName(name string) Value

FieldByName 返回具有给定名称的结构字段。它返回 如果没有找到字段,则为零值。如果 v 的 Kind 不是,它会恐慌 结构。

喜欢这个工作示例代码:

package main

import "fmt"
import "reflect"

func main() {
    person := Person{}
    person.name = "testing"
    person.address.street = "123 test st"
    person.address.city = "tester"
    person.address.zip = 90210
    person.billing.address.same = true

    v := reflect.ValueOf(person)
    f := v.FieldByName("address")
    key := f.FieldByName("zip")
    fmt.Println(key)                   // 90210
    fmt.Println(f.FieldByName("city")) // tester    
}

type Person struct {
    name    string
    address Address
    billing Billing
}
type Billing struct {
    address Address
}
type Address struct {
    street, city string
    zip          int
    same         bool
}

输出:

90210
tester

对于您的特殊情况,您可以使用fmt.Println(field(person, "person.address.zip")),就像这个工作示例代码(仅用于演示):

package main

import "fmt"
import "reflect"
import "strings"

func field(t interface{}, key string) reflect.Value {
    strs := strings.Split(key, ".")
    v := reflect.ValueOf(t)
    for _, s := range strs[1:] {
        v = v.FieldByName(s)
    }
    return v
}
func main() {
    person := Person{}
    person.name = "testing"
    person.address.street = "123 test st"
    person.address.city = "tester"
    person.address.zip = 90210
    person.billing.address.same = true

    fmt.Println(field(person, "person.address.zip"))  //90210
    fmt.Println(field(person, "person.address.city")) //tester
}

type Person struct {
    name    string
    address Address
    billing Billing
}
type Billing struct {
    address Address
}
type Address struct {
    street, city string
    zip          int
    same         bool
}

输出:

90210
tester

【讨论】:

    【解决方案2】:

    我不熟悉 protobuf 的内部结构,或者它是否提供任何方法来做到这一点。

    但是,(1) 如果您想以您描述的方式读取值 - 通过动态链接字段并且 (2) 您希望读取它的次数超过一度;我只是将它序列化为 json 并使用this 包。它非常快,并且(几乎)为您提供了您想要的相同语义:

    // assuming your object got marshaled to this for example
    json := `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
    value := gjson.Get(json, "name.last")
    println(value.String())
    

    【讨论】:

      猜你喜欢
      • 2013-07-08
      • 1970-01-01
      • 2018-03-28
      • 2019-12-28
      • 1970-01-01
      • 2013-09-19
      • 2011-12-12
      • 1970-01-01
      • 2014-07-06
      相关资源
      最近更新 更多