Go言語のリフレクションによる構造体のフィールド操作とメソッド呼び出し

Go言語の標準ライブラリであるreflectパッケージを使用すると、静的に型付けされた構造体のフィールド値を実行時に読み取ったり、メソッドを動的に呼び出したりすることができます。ここでは、構造体のインスタンスからリフレクション情報を取得し、フィールドの列挙やメソッドの実引数を介した呼び出しを行う具体的な実装例を示します。

構造体とメソッドの定義

まず、操作対象となるServer構造体と、それに紐づくいくつかのメソッドを定義します。フィールドの更新を正しく行うため、値を変更するメソッドはポインタレシーバを使用します。

package main

import (
	"fmt"
	"reflect"
)

// Server は検査対象の構造体です
type Server struct {
	ID   string
	Port int
}

// ShowStatus は構造体の現在の状態を出力します
func (s Server) ShowStatus() {
	fmt.Printf("[Status] ID: %s, Port: %d\n", s.ID, s.Port)
}

// UpdateConfig はIDとPortを更新します(ポインタレシーバを使用)
func (s *Server) UpdateConfig(newID string, newPort int) {
	s.ID = newID
	s.Port = newPort
}

// CalculateCapacity は引数の合計値を返す計算メソッドです
func (s Server) CalculateCapacity(base, load int) int {
	return base + load
}

リフレクションを用いた操作の実装

以下の関数InspectStructは、interface{}型で受け取った任意のオブジェクトに対してリフレクションを行います。reflect.ValueOfを用いて値を取得し、フィールドの反復処理やメソッドの呼び出しを実行します。

メソッドの呼び出しには注意が必要です。MethodByNameやインデックスベースのアクセスを使用し、引数は[]reflect.Valueスライスとして渡す必要があります。また、メソッドはASCII順(アルファベット順)にソートされてインデックスが割り当てられます。

func InspectStruct(entity interface{}) {
	rVal := reflect.ValueOf(entity)

	// 1. フィールド情報の取得
	fmt.Println("--- Field Inspection ---")
	fieldCount := rVal.NumField()
	for i := 0; i < fieldCount; i++ {
		field := rVal.Field(i)
		fmt.Printf("Field %d (%s): %v\n", i, field.Type(), field.Interface())
	}

	// 2. メソッド情報の取得と呼び出し
	fmt.Println("\n--- Method Invocation ---")
	methodCount := rVal.NumMethod()
	fmt.Printf("Total Methods: %d\n", methodCount)

	// メソッドは名前の順序でインデックスが決定されます:
	// 0: CalculateCapacity (C)
	// 1: ShowStatus (S)
	// 2: UpdateConfig (U)

	// CalculateCapacity (引数あり, 戻り値あり) の呼び出し
	capacityArgs := []reflect.Value{
		reflect.ValueOf(100),
		reflect.ValueOf(50),
	}
	result := rVal.Method(0).Call(capacityArgs)
	fmt.Printf("Calculated Capacity: %d\n", result[0].Int())

	// ShowStatus (引数なし) の呼び出し
	rVal.Method(1).Call(nil)

	// UpdateConfig (引数あり) の呼び出し
	// ポインタレシーバ経由で呼び出すため、元の構造体の値が変更されます
	configArgs := []reflect.Value{
		reflect.ValueOf("Prod-Server-01"),
		reflect.ValueOf(8080),
	}
	rVal.Method(2).Call(configArgs)

	// 変更後の状態を確認
	fmt.Println("After UpdateConfig:")
	rVal.Method(1).Call(nil)
}

func main() {
	// 構造体の初期化
	svr := Server{
		ID:   "Dev-Server",
		Port: 3000,
	}

	// ポインタを渡してリフレクションを実行
	InspectStruct(&svr)
}

タグ: Go golang Reflection struct reflectパッケージ

5月30日 00:37 投稿