按名称访问struct属性

Nic*_*DIA 59 go go-reflect

这是一个不起作用的简单go程序:

package main
import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getProperty(&v, "X"))
}

func getProperty(v *Vertex, property string) (string) {
    return v[property]
}
Run Code Online (Sandbox Code Playgroud)

错误:

prog.go:18:无效操作:v [property](类型*Vertex的索引)

我想要的是使用其名称访问Vertex X属性.如果我这样做v.X,但v["X"]没有.

谁能告诉我如何使这项工作?

Pau*_*kin 93

大多数代码不应该需要这种动态查找.与直接访问相比,它是低效的(编译器知道Vertex结构中X字段的偏移,它可以将vX编译为单个机器指令,而动态查找将需要某种哈希表实现或类似).它还禁止静态类型:编译器无法检查您是否未尝试动态访问未知字段,并且无法知道结果类型应该是什么.

但是......语言提供了一个反射模块,用于您需要的罕见时间.

package main

import "fmt"
import "reflect"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getField(&v, "X"))
}

func getField(v *Vertex, field string) int {
    r := reflect.ValueOf(v)
    f := reflect.Indirect(r).FieldByName(field)
    return int(f.Int())
}
Run Code Online (Sandbox Code Playgroud)

这里没有错误检查,所以如果你要求一个不存在的字段,或者字段不是int类型,你会感到恐慌.有关详细信息,请查看文档以获取反映.

  • +1,并参见[反思法则](http://blog.golang.org/laws-of-reflection),其中介绍了这个想法. (2认同)
  • 这个反射模块有点棘手。我尝试使用它但没有成功。似乎我忘记调用 `Ìndirect`。感谢您的工作示例和所有解释。非常感谢 :-) (2认同)
  • 感谢您对代码的解释。对我来说,它比代码本身更有用! (2认同)

Von*_*onC 10

您现在拥有项目oleiade/reflections,它允许您在struct value或指针上获取/设置字段.
它使得使用reflect不那么棘手.

s := MyStruct {
    FirstField: "first value",
    SecondField: 2,
    ThirdField: "third value",
}

fieldsToExtract := []string{"FirstField", "ThirdField"}

for _, fieldName := range fieldsToExtract {
    value, err := reflections.GetField(s, fieldName)
    DoWhatEverWithThatValue(value)
}


// In order to be able to set the structure's values,
// a pointer to it has to be passed to it.
_ := reflections.SetField(&s, "FirstField", "new value")

// If you try to set a field's value using the wrong type,
// an error will be returned
err := reflection.SetField(&s, "FirstField", 123)  // err != nil
Run Code Online (Sandbox Code Playgroud)


Mic*_*ael 8

我想提供一种不使用反射的不同方法:

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getProperty(&v, "X"))
}

type Getter func(v *Vertex) int

var VertexAccess = map[string]Getter{
    "X": func(v *Vertex) int { return v.X },
    "Y": func(v *Vertex) int { return v.Y },
}

func getProperty(v *Vertex, property string) int {
    return VertexAccess[property](v)
}
Run Code Online (Sandbox Code Playgroud)

https://go.dev/play/p/2E7LZBWx7yZ

这是一个O(1)映射查找和函数调用,其性能应该比反射更好。显然,您需要为您想要支持的每种类型提供一些脚手架代码。从好的方面来说,您可以轻松地重构代码;您的功能是https://martinfowler.com/bliki/TellDontAsk.htmlgetProperty的潜在反模式