GoLang リフレクション

この記事の一部は機械翻訳を使ってるよ

Golang シリーズ

Hello GoLang: https://blog.yexca.net/ja/archives/154
GoLang (var and const) 変数と定数: https://blog.yexca.net/ja/archives/155
GoLang (func) 関数: https://blog.yexca.net/ja/archives/156
GoLang (slice and map) スライス: https://blog.yexca.net/ja/archives/160
GoLang (OOP) オブジェクト指向: https://blog.yexca.net/ja/archives/162
GoLang (reflect) リフレクション: この記事
GoLang (struct tag) 構造タグ: https://blog.yexca.net/ja/archives/205
GoLang (goroutine) ゴルーチン: https://blog.yexca.net/ja/archives/206
GoLang (channel) チャンネル: https://blog.yexca.net/ja/archives/207


反射指の一種の用途であり、自己記述と自己制御が可能です

pair

Go 言語变量には type と value の部分が含まれ、pair で構成されます

image

static type はコード実行時に見られるタイプであり、concrete type は runtime システムに見られるタイプです。

このタイプは、static type ではなく変数の concrete type に応じて成功するかどうかを判断します。したがって、concrete type が write メソッドの命令も実装している場合、タイプは write として承認される可能性があります。

反射はタイプに基づいて確立され、静的タイプであり、固定されているため、反射は主に interface タイプに関係します (concrete type)

package main

import "fmt"

type ReadBook interface {
    Read()
}
type WriteBook interface {
    Write()
}

type Book struct {
}

func (this *Book) Read() {
    fmt.Println("read Book")
}
func (this *Book) Write() {
    fmt.Println("write Book")
}

func main() {
    // b: pair<type:Book, value:book{} のアドレス>
    b := &Book{}

    // r: pair<type:, value:>
    var r ReadBook
    // r: pair<type:Book, value:book{} のアドレス>
    r = b
    r.Read()

    var w WriteBook
    w = r.(WriteBook) //  r のタイプは Book であるため
    w.Write()
}

TypeOf と ValueOf

Reflect.TypeOf() は pair 中の type を取得します、reflect.ValueOf() は pair 中の value を取得します

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := 3.14
    fmt.Println("type of a:", reflect.TypeOf(a)) // float64
    fmt.Println("value of a:", reflect.ValueOf(a)) // 3.14
}

型转换

reflect.ValueOf() を実行すると、型が reflect.Value 变量になります。

既知原データの種類

既知原データ型は直接强制转换可能

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := 3.14
    fmt.Println("type of a:", reflect.TypeOf(a))
    fmt.Println("value of a:", reflect.ValueOf(a))

    value := reflect.ValueOf(a)
    fmt.Printf("type of value:%T\n", value) // reflect.Value
    newA := value.Interface().(float64)
    fmt.Printf("type of newA:%T\n", newA) // float64
}

型変換は完全に一貫している必要があることに注意することが重要です。そうでないと、ポインタなどの panic が発生します。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := 3.14
    fmt.Println("type of a:", reflect.TypeOf(a))
    fmt.Println("value of a:", reflect.ValueOf(a))

    // 輸送先住所
    point := reflect.ValueOf(&a)
    fmt.Printf("type of value:%T\n", point) // reflect.Value
    // 型をポインタに変換する
    newA := point.Interface().(*float64)
    fmt.Printf("type of newA:%T\n", newA) // *float64
}

つまり、反射することができます “反射型オブジェクト” 再再再转换は “インターフェース変数”

未知原データ型

Field を横断して取得

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    // 一行は 1 つの field です
    Name string
    Age  int
    Rank float64
}

// メソッドはパブリックであることに注意してください。プライベートの場合はアクセスできません。
func (person Person) Sleep() {
    fmt.Println("person sleep")
}

func main() {
    p := Person{"zhangSan", 18, 5.2}
    getFieldAndMethod(p)
}

func getFieldAndMethod(input interface{}) {
    getType := reflect.TypeOf(input)
    fmt.Println("type:", getType.Name())

    getValue := reflect.ValueOf(input)
    fmt.Println("value/AllField:", getValue)

    // 属性を取得します
    numField := getValue.NumField()
    for i := 0; i < numField; i++ {
        fieldType := getType.Field(i)
        fieldValue := getValue.Field(i).Interface()
        fmt.Printf("%s: %v = %v\n", fieldType.Name, fieldType.Type, fieldValue)
    }

    // メソッドを取得します
    numMethod := getType.NumMethod()
    for i := 0; i < numMethod; i++ {
        method := getType.Method(i)
        fmt.Printf("%s: %v\n", method.Name, method.Type)
    }
}

reflect.Value による割り当て

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := 3.14

    // ポインタの場合のみ割り当て可能
    pointer := reflect.ValueOf(&a)
    // 元の値を取得するためのアドレスを設定します
    newValue := pointer.Elem()
    
    // 値が設定可能かどうかをチェックする
    fmt.Println("value canSet:", newValue.CanSet())
    if newValue.CanSet() {
        // 設定できる場合
        newValue.SetFloat(9.96)
        // 変数の値を表示
        fmt.Println("value of a:", a) // 9.96
    } else {
        fmt.Println("error")
    }
}

reflect.Value 経由でメソッドを呼び出す

関数名による呼び出し

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    name string
    age  int
}

func (user User) MethodHasArgs(a string, b int) {
    fmt.Println("User MethodHasArgs")
}
func (user User) MethonNotArgs() {
    fmt.Println("User MethodNotArgs")
}

func main() {
    user := User{"zhangSan", 18}
    value := reflect.ValueOf(user)

    // 関数名で呼び出す
    method1 := value.MethodByName("MethodHasArgs")
    // ビルドパラメータ
    args1 := []reflect.Value{reflect.ValueOf("string"), reflect.ValueOf(18)}
    // 関数の呼び出し
    method1.Call(args1)

    // パラメータなしで呼び出す
    method2 := value.MethodByName("MethonNotArgs")
    args2 := make([]reflect.Value, 0)
    method2.Call(args2)

}

リフレクションの基本原則

image

This post is licensed under CC BY-NC-SA 4.0 by the author.
最終更新 2025-01-28 20:49 +0900

Hugo で構築されています。 | テーマ StackJimmy によって設計されています。