web-dev-qa-db-ja.com

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

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

 type t struct {fi int; fs string} 
 var r t = t {123、 "jblow"} 
 var i64 int64 = 456 
  1. フィールドiの名前の取得-これは機能しているようです

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

  2. フィールドiの値をa)interface {}、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 )

    パニック: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 )

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


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

四。これは動作します:

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

また、フィールド名はエクスポート可能でなければならないことを文書化します(大文字で始まる)

87
cc young

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

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


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

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

func (v Value) CanAddr() bool

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

Go reflect パッケージには CanSet 関数があり、trueの場合、CanAddrtrueです。

func (v Value) CanSet() bool

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

Setフィールドをstructできることを確認する必要があります。例えば、

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)
}
128
peterSO

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

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
11
Asgeir