リフレクトを使用して、構造体フィールドの値をどのように設定しますか?


106

reflectパッケージを使用して構造体フィールドを操作するのに大まかな時間がかかります。特に、フィールド値の設定方法を理解していません。

タイプt構造体{fi int; fs string}
var rt = t {123、 "jblow"}
var i64 int64 = 456
  1. フィールド名iを取得-これは動作するようです

    var field = reflect.TypeOf(r).Field(i).Name

  2. フィールドiの値をa)インターフェイス{}、b)intとして取得-これは機能するようです

    var iface interface{} = reflect.ValueOf(r).Field(i).Interface()

    var i int = int(reflect.ValueOf(r).Field(i).Int())

  3. フィールドiの値の設定-1つ試す-パニック

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    panic:エクスポートされていないフィールドを使用して取得した値を使用するreflect.Value・SetInt

    フィールド名「id」と「name」が好きではなかったので、「Id」と「Name」に名前が変更されました

    a)この仮定は正しいですか?

    b)正しい場合、同じファイル/パッケージ内にあるため必要ではないと考えた

  4. フィールドiの値を設定-2つ試す(フィールド名を大文字にして)-パニック

    reflect.ValueOf(r).Field(i).SetInt( 465 )

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    panic:reflect.Value・アドレス指定不可能な値を使用したSetInt


@peterSOによる以下の指示は、徹底的で高品質です

四。これは機能します:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

彼はまた、フィールド名がエクスポート可能である必要があることを文書化します(先頭は大文字)


reflectデータの設定に使用している人に最も近い例は、comments.gmane.org / json.Unmarshal
cc young

(上記のコメントは廃止されました)
cc young

回答:


156

囲碁は オープンソースコードます。リフレクションについて学ぶ良い方法は、コアのGo開発者がリフレクションをどのように使用するかを確認することです。たとえば、Go fmtおよびjsonパッケージ。パッケージのドキュメントには、パッケージファイルという見出しの下にソースコードファイルへのリンクがあります。

Go jsonパッケージは、JSONをGo構造との間でマーシャリングおよびアンマーシャリングします。


structエラーを慎重に回避しながらフィールドの値を設定するステップバイステップの例を次に示します。

Go reflectパッケージにはCanAddr関数があります。

func (v Value) CanAddr() bool

CanAddrは、値のアドレスがAddrで取得できる場合はtrueを返します。このような値はアドレス可能と呼ばれます。スライスの要素、アドレス可能な配列の要素、アドレス可能な構造体のフィールド、またはポインターの逆参照の結果である場合、値はアドレス可能です。CanAddrがfalseを返す場合、Addrを呼び出すとパニックになります。

Go reflectパッケージにはCanSet関数があり、その関数は、もしあればtrue、それCanAddrもそうtrueです。

func (v Value) CanSet() bool

CanSetは、vの値を変更できる場合にtrueを返します。値は、アドレス指定可能で、エクスポートされていない構造体フィールドを使用して取得されなかった場合にのみ変更できます。CanSetがfalseを返す場合、Setまたはタイプ固有のセッター(SetBool、SetInt64など)を呼び出すとパニックが発生します。

私たちSetstructフィールドにできることを確認する必要があります。例えば、

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    // N at start
    fmt.Println(n.N)
    // pointer to struct - addressable
    ps := reflect.ValueOf(&n)
    // struct
    s := ps.Elem()
    if s.Kind() == reflect.Struct {
        // exported field
        f := s.FieldByName("N")
        if f.IsValid() {
            // A Value can be changed only if it is 
            // addressable and was not obtained by 
            // the use of unexported struct fields.
            if f.CanSet() {
                // change value of N
                if f.Kind() == reflect.Int {
                    x := int64(7)
                    if !f.OverflowInt(x) {
                        f.SetInt(x)
                    }
                }
            }
        }
    }
    // N at end
    fmt.Println(n.N)
}

Output:
42
7

すべてのエラーチェックが不要であることを確認できる場合、例は次のように簡略化されます。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    fmt.Println(n.N)
    reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
    fmt.Println(n.N)
}

5
あきらめる!どこかに答えがありますが、json pkgでの4時間の作業ではそれが得られませんでした。リフレクトpkgの場合、情報のプルはかなり簡単ですが、データの設定には、どこかに簡単な例を示したいと思う黒魔術が必要です。
ccヤング

2
未解決の!あなたがタイにいるなら、私にあなたにビールか2つか3つをあなたに接待させてください!どうもありがとうございました
cc young

5
素晴らしい実用的な例として、この記事は私にそれを完全にわかりやすく説明
danmux

素晴らしいexample.Hereは、同じコードの遊び場サンプルあるplay.golang.org/p/RK8jR_9rPh
Sarath Sadasivan Pillaiさん

これは構造体フィールドを設定しません。これは、構造体へのポインタにフィールドを設定します。明らかに、これはOPが尋ねた質問に答えていません。
wvxvw 2017年

13

これはうまくいくようです:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    Number int
    Text string
}

func main() {
    foo := Foo{123, "Hello"}

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

    reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}

プリント:

123
321

ありがとう!peterSOのメモを読んだ今、これは完全に理にかなっています。私は&fooではなくfooを使用していたため、変更できず、Elem()が何であるか不明でした。
ccヤング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.