91

这是一个不工作的简单 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[property](*Vertex 类型的索引)

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

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

4

4 回答 4

130

大多数代码不应该需要这种动态查找。与直接访问相比,它效率低下(编译器知道 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 类型,你会感到恐慌。检查文档以获取更多详细信息。

于 2013-09-21T09:27:13.363 回答
20

您现在拥有项目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
于 2014-09-02T05:26:37.900 回答
1

使用getAttr,您可以轻松获取和设置。

package main

import (
    "fmt"
    "reflect"
)

func getAttr(obj interface{}, fieldName string) reflect.Value {
    pointToStruct := reflect.ValueOf(obj) // addressable
    curStruct := pointToStruct.Elem()
    if curStruct.Kind() != reflect.Struct {
        panic("not struct")
    }
    curField := curStruct.FieldByName(fieldName) // type: reflect.Value
    if !curField.IsValid() {
        panic("not found:" + fieldName)
    }
    return curField
}

func main() {
    type Point struct {
        X int
        y int  // Set prefix to lowercase if you want to protect it.
        Z string
    }

    p := Point{3, 5, "Z"}
    pX := getAttr(&p, "X")

    // Get test (int)
    fmt.Println(pX.Int()) // 3

    // Set test
    pX.SetInt(30)
    fmt.Println(p.X)  // 30

    // test string
    getAttr(&p, "Z").SetString("Z123")
    fmt.Println(p.Z)  // Z123

    py := getAttr(&p, "y")
    if py.CanSet() { // The necessary condition for CanSet to return true is that the attribute of the struct must have an uppercase prefix
        py.SetInt(50) // It will not execute here because CanSet return false.
    }
    fmt.Println(p.y) // 5
}

运行它 去游乐场

参考

于 2021-03-04T07:16:23.747 回答
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)
}
于 2020-06-18T12:12:17.597 回答