1. ホーム
  2. go

[解決済み] reflectを使用して、構造体フィールドの値を設定するにはどうすればよいですか?

2022-08-11 10:24:05

質問

を使って構造体フィールドを扱うのに苦労しています。 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 )

    パニック : unexported フィールドを使用して取得した値を使用する 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 さんの指示は、徹底的で高品質です。

4.これはうまくいく。

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

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

どのように解決するのですか?

Go jsonパッケージは、Go構造体からのJSONのマーシャルとアンマーシャル、およびGo構造体へのJSONのマーシャルとアンマーシャルを行います。

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

碁は reflect パッケージには CanAddr という関数があります。

func (v Value) CanAddr() bool

CanAddrは、値のアドレスがAddrで取得できる場合に真を返します。 のアドレスがAddrで取得できる場合は真を返します。 このような値はaddressableと呼ばれます。A 値は、それがスライスの要素である場合、アドレス指定可能です。 スライスの要素、アドレス指定可能な配列の要素、または アドレス指定可能な配列の要素、アドレス指定可能な構造体のフィールド アドレス指定可能な構造体のフィールド、またはポインタのデリファレンスの結果であれば ポインタの参照解除の結果であれば,アドレス指定可能です。もしCanAddr が偽を返した場合,Addrを呼び出すと を呼び出すとパニックになります。

Goは reflect パッケージには CanSet 関数があり、もし true の場合、以下のようになります。 CanAddr はまた true .

func (v Value) CanSet() bool

CanSetは、vの値が変更可能であれば真を返します。 の値が変更可能であれば真を返します。値を変更できるのは アドレス指定可能で、かつ アドレス指定が可能で、かつ、未エクスポートの 構造体フィールドの使用によって取得されたものでない場合に限り、 変更できる。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)
}

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