LoginSignup
0
0

More than 3 years have passed since last update.

【Go】ポインタ変数を reflect パッケージで動的に操作する

Last updated at Posted at 2020-04-13

Javaとかオブジェクト指向言語のリフレクションと同じ感覚で操作しようとすると結構ハマりますよね、golangのreflect。

特にポインタの場合、一味工夫が必要だったりするので、現場で使いそうな操作をまとめておきます。(これが更に "構造体の中のフィールド" だったりするとまた色々あるのですが、また後日にでも。)

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

func main() {
    s := "Aerith"

    print("************* ポインタを reflect パッケージで色々いじくってみる ***********")

    ps := &s
    print(*ps)  // Aerith

    //ps のreflect.Value構造体を取得
    psValue := reflect.ValueOf(ps)
    print(psValue.String())  // <*string Value>

    //ps のreflect.Type構造体を取得
    psType := reflect.TypeOf(ps)
    print(psType.String())  // *string

    //reflect.Type構造体をreflect.Value経由で取得
    psTypeFromValue := psValue.Type()
    print(psTypeFromValue.String())  // *string

    //ポインタの型が *string かどうかを判定する
    var stringPointer *string
    typeOfStringPointer := reflect.TypeOf(stringPointer)
    print(strconv.FormatBool(psType == typeOfStringPointer))  // true

    //ps のreflect.Kind値を取得
    psKind := psType.Kind()
    print(psKind.String())  // ptr

    //psがポインタであるかどうかのbool値を取得
    psIsPointer := psKind == reflect.Ptr
    print(strconv.FormatBool(psIsPointer))  // true


    //ps のアドレス番号を取得
    //(※ アドレス番号自体は実行するたびに変わったりします)
    print(strconv.FormatUint(uint64(psValue.Pointer()), 16))  // c0000401f0

    //逆に、reflect.Value構造体から新たなポインタを作成する
    psCopy := psValue.Interface().(*string)
    print(*psCopy)  // Aerith

    //同じアドレス c0000401f0 を指し示す入れ物 psCopy が増えただけ
    psCopyValue := reflect.ValueOf(psCopy)
    print(strconv.FormatUint(uint64(psCopyValue.Pointer()), 16))  // c0000401f0

    //新しい入れ物 psCopy に違うアドレスを代入しても、コピー元の入れ物 ps には影響ない
    s2 := "Barret"
    psCopy = &s2
    psCopyValue = reflect.ValueOf(psCopy)
    print(*psCopy)  // Barret
    print(strconv.FormatUint(uint64(psCopyValue.Pointer()), 16))  // c0000402a0

    print(*ps)  // Aerith
    print(strconv.FormatUint(uint64(psValue.Pointer()), 16))  // c0000401f0


    print("********* ここからは、ポインタアドレスが指し示す先の値をダイレクトにいじくってみる *********")

    //ポインタ ps が指し示す先の値のreflect.Value構造体を取得
    psElemValue := psValue.Elem()
    print(psElemValue.String())  // Aerith

    //ポインタ ps が指し示す先の値のreflect.Type構造体を取得
    psElemType := psElemValue.Type()
    print(psElemType.String())  // string

    //ポインタ ps が指し示す先の値のreflect.Kind値を取得
    psElemKind := psElemType.Kind()
    print(psElemKind.String())  // string

    //ポインタ ps が指し示す先の値を書き換える
    psElemValue.SetString("Cloud")
    print(psElemValue.String())  // Cloud

    // *ps がちゃんと書き換わっている
    print(*ps)  // Cloud

    //大本の変数 s もちゃんと書き換わっている
    print(s)  // Cloud

    //逆に、reflect.Value構造体から新たな変数を作成する
    sCopyInterface := psElemValue.Interface()
    sCopy := sCopyInterface.(string)
    print(sCopy)  // Cloud

    //ポインタが指し示す先の値は同じでも、メモリ上のデータとしては別物になっている
    sCopyPointer := &sCopy
    sCopyValue := reflect.ValueOf(sCopyPointer)
    print(strconv.FormatUint(uint64(sCopyValue.Pointer()), 16))  // c0000402c0

    //ポインタ sCopyPointer が指し示す先の値を書き換える
    sCopyValue.Elem().SetString("Tifa")
    print(sCopyValue.Elem().String())  // Tifa

    //大本の変数 sCopy もちゃんと書き換わっている
    print(sCopy)  // Tifa

    //コピー元の変数 s は変わっていない
    print(s) // Cloud


    print("****************** ポインタがnilの場合 ******************")

    //nilポインタが指し示す先の値を reflect で書き換えることはできない
    sCopyPointer = nil
    sCopyValue = reflect.ValueOf(sCopyPointer)
    print(sCopyValue.String())  // <*string Value>
    print(strconv.FormatBool(sCopyValue.CanSet()))  // false

    sCopyKind := sCopyValue.Kind()
    print(sCopyKind.String())  // ptr

    //ポインタが nil かどうかを判定する
    print(strconv.FormatBool(sCopyValue.IsNil()))  // true

    //ポインタが指し示す先の値は <invalid Value>
    sCopyElemValue := sCopyValue.Elem()
    print(sCopyElemValue.String())  // <invalid Value>

    sCopyElemKind := sCopyElemValue.Kind()
    print(sCopyElemKind.String())  // invalid

    //reflect.Type が取得不可能
    sCopyElemValue.Type()  // panic発生
    /*
        panic: reflect: call of reflect.Value.Type on zero Value

        goroutine 1 [running]:
        reflect.Value.Type(0x0, 0x0, 0x0, 0x1, 0x1)
            /go/src/reflect/value.go:1877 +0x16d
        reflect_pointer.main()
            /golang/workspace/awesomeProject/main/reflect_pointer.go:110 +0x1ce2
    */
}

func print(s interface{}) {
    fmt.Println(s)
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0