124

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

タイプtstruct{fi int; fs文字列}
var rt = 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の設定値-試してみてください-パニック

    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 )

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

4

2 に答える 2

181

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

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

GoreflectパッケージにはCanAddr機能があります。

func (v Value) CanAddr() bool

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

Goreflectパッケージには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)
}

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

于 2011-06-18T14:29:22.170 に答える
16

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

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
于 2011-06-19T13:27:32.690 に答える