web-dev-qa-db-ja.com

名前で構造体プロパティにアクセス

動作していない単純な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"]しません。

誰かがこの仕事をする方法を教えてもらえますか?

56
Nicolas BADIA

ほとんどのコードでは、このような動的なルックアップは必要ありません。直接アクセスと比較すると非効率です(コンパイラはVertex構造のXフィールドのオフセットを知っているため、v.Xを単一のマシン命令にコンパイルできますが、動的ルックアップにはハッシュテーブルの実装などが必要です)。また、静的型付けも禁止します。コンパイラは、未知のフィールドに動的にアクセスしようとしていないことを確認する方法がなく、結果の型が何であるかを知ることができません。

しかし...この言語は、まれにこれが必要な場合に reflect モジュールを提供します。

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型ではない場合、パニックが発生します。詳細については、 reflectのドキュメント を確認してください。

86
Paul Hankin

これで、プロジェクト oleiade/reflections ができました。これにより、構造体値のフィールドを取得または設定できます。ポインター。
reflect package の使用はトリッキーになります。

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
10
VonC