通过名称访问结构属性


76

这是一个不起作用的简单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]
}

错误:

prog.go:18:无效的操作:v [属性](*顶点类型的索引)

我想要的是使用名称访问Vertex X属性。如果我这样做,v.X它会起作用,但v["X"]不会。

有人可以告诉我如何进行这项工作吗?

Answers:


118

大多数代码都不需要这种动态查找。与直接访问相比,它效率低下(编译器知道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())
}

这里没有错误检查,因此如果您请求不存在的字段或该字段的类型不是int,您将感到恐慌。检查文档以了解更多详细信息。


2
+1,另请参阅《反射定律》,其中对该思想作了介绍。
Fred Foo 2013年

1
这个反射模块有点棘手。我尝试使用它没有成功。看来我忘了打电话Ìndirect。感谢您的工作示例和所有解释。非常感谢:-)
Nicolas BADIA 2013年

2
感谢您对代码的解释。对我来说,它比代码本身更有用!
Nebulosar

15

现在,您有了项目oleiade / reflections,该项目可让您获取/设置结构值或指针上的字段。
它使使用该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

0

您可以封送该结构并将其取消封送回map[string]interface{}。但是,它将所有数值转换为,float64因此您必须int手动将其转换为。

type Vertex struct {
    X int
    Y int
}

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

func getProperty(v *Vertex, property string) float64 {
    m, _ := json.Marshal(v)
    var x map[string]interface{}
    _ = json.Unmarshal(m, &x)
    return x[property].(float64)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.