三十的博客

Go 反射学习01-反射基础

发布时间
阅读量 加载中...

什么是反射

反射(Reflection)是指在程序运行时能够检查自身结构的能力。

Go 语言通过 reflect 包提供了反射功能,允许程序在运行时检查变量的类型和值,甚至修改它们

反射的主要用途包括:

反射基础

.Type/.Value

Go 反射的核心是两个类型:

reflect.TypeOf:获取变量的类型,返回 reflect.Type 类型。

reflect.ValueOf:获取变量的值,返回 reflect.Value 类型。

reflect.Value 是一个结构体类型,通过 reflect.Value 可以获取到关于该变量的很多信息,比如类型、值、方法等。

go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	var x float64 = 3.4
	fmt.Println("type:", reflect.TypeOf(x))   // type: float64
	fmt.Println("value:", reflect.ValueOf(x)) // value: 3.4
}

获取原始值

可以使用 Value 类型的 Interface() 方法获取原始值:

go
v := reflect.ValueOf(x)
y := v.Interface().(float64) // 类型断言
fmt.Println(y) // 3.4

反射的三大法则

  1. 从接口值到反射对象reflect.TypeOfreflect.ValueOf 将接口值转换为反射对象
  2. 从反射对象到接口值reflect.Value.Interface() 将反射对象转换回接口值
  3. 要修改反射对象,其值必须可设置:反射对象必须持有原始值的指针才能修改它

反射简单示例

检查类型和值

go
package main

import (
	"fmt"
	"reflect"
)

// inspectVariable 检查变量
func inspectVariable(v interface{}) {
	t := reflect.TypeOf(v)
	fmt.Println("Type:", t)
	fmt.Println("Kind:", t.Kind())

	val := reflect.ValueOf(v)
	fmt.Println("Value:", val)

	if t.Kind() == reflect.Int {
		fmt.Println("It's an integer with value:", val.Int())
	}
}

func main() {
	inspectVariable(42)
	inspectVariable("hello")
	inspectVariable([]int{1, 2, 3})
}

运行的结果为:

go
// Type: int
// Kind: int
// Value: 42
// It's an integer with value: 42
// Type: string
// Kind: string
// Value: hello
// Type: []int
// Kind: slice
// Value: [1 2 3]

修改值

go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	x := 10
	v := reflect.ValueOf(&x).Elem() // 获取可设置的Value

	fmt.Println("Original value:", x) // Original value: 10
	v.SetInt(20)
	fmt.Println("Modified value:", x) // Modified value: 20
}

调用函数

go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	// 定义一个加法函数
	add := func(a, b int) int {
		return a + b
	}

	// 通过反射调用
	v := reflect.ValueOf(add)
	args := []reflect.Value{
		reflect.ValueOf(10),
		reflect.ValueOf(20),
	}

	result := v.Call(args)
	fmt.Println("Result:", result[0].Int()) // 30
}

结构体反射

go
package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	p := Person{"Alice", 30}
	v := reflect.ValueOf(p)
	t := v.Type()

	for i := 0; i < v.NumField(); i++ {
		field := t.Field(i)
		value := v.Field(i)
		fmt.Printf("%s (%s) = %v\n", field.Name, field.Type, value.Interface())
	}
}

运行的结果为:

go
// Name (string) = Alice
// Age (int) = 30

反射的注意事项

实际应用示例

遍历结构体

使用反射来遍历结构体的字段、调用结构体的方法、并获取结构体标签的值。

go
package main

import (
	"fmt"
	"reflect"
)

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"monster_age"`
	Score float32
	Sex   string
}

func (s Monster) Print() {
	fmt.Println("---- start ----")
	fmt.Println(s)
	fmt.Println("---- end ----")
}

func (s Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

func (s Monster) Set(name string, age int, score float32, sex string) {
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

func TestStruct(a interface{}) {
	typ := reflect.TypeOf(a)
	val := reflect.ValueOf(a)
	kd := val.Kind()

	if kd != reflect.Struct {
		fmt.Println("expect struct")
		return
	}

	numField := val.NumField()
	fmt.Printf("struct has %d fields\n", numField)

	for i := 0; i < numField; i++ {
		fmt.Printf("field %d: 值为=%v\n", i, val.Field(i))

		tagVal := typ.Field(i).Tag.Get("json")
		if tagVal != "" {
			fmt.Printf("field %d: tag为=%v\n", i, tagVal)
		}
	}

	numOfMethod := val.NumMethod()
	fmt.Printf("struct has %d methods \n", numOfMethod)

	// 方法的排序是默认按照 函数名排序(ASCII码)
	val.Method(1).Call(nil) // 获取到第二个方法 调用它

	// 调用结构体第1个方法
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Method(0).Call(params)
	fmt.Println("res=", res[0].Int()) // 返回结果 返回的结果是 []reflect.Value
}

func main() {
	var a Monster = Monster{
		Name:  "黄鼠狼",
		Age:   400,
		Score: 30.1,
		Sex:   "男",
	}

	TestStruct(a)
}

运行的结果为:

go
// struct has 4 fields
// field 0: 值为=黄鼠狼
// field 0: tag为=name
// field 1: 值为=400
// field 1: tag为=monster_age
// field 2: 值为=30.1
// field 3: 值为=男
// struct has 3 methods
// ---- start ----
// {黄鼠狼 400 30.1 男}
// ---- end ----
// res= 50

遍历结构体 2

使用反射的方式来获取结构体的标签、遍历字段的值、修改字段值、调用结构体方法。

go
package main

import (
	"fmt"
	"reflect"
)

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"monster_age"`
	Score float32
	Sex   string
}

func (s Monster) Print() {
	fmt.Println("---- start ----")
	fmt.Println(s)
	fmt.Println("---- end ----")
}

func (s Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

func (s Monster) Set(name string, age int, score float32, sex string) {
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

func TestStruct(a interface{}) {
	typ := reflect.TypeOf(a)
	val := reflect.ValueOf(a)
	kd := val.Kind()

	if kd != reflect.Struct && kd != reflect.Ptr {
		fmt.Println("expect struct")
		return
	}

	numField := val.Elem().NumField()
	fmt.Printf("struct has %d fields\n", numField)

	// 修改 name 属性值
	val.Elem().Field(0).SetString("嘿嘿嘿")

	for i := 0; i < numField; i++ {
		fmt.Printf("field %d: 值为=%v\n", i, val.Elem().Field(i))

		tagVal := typ.Elem().Field(i).Tag.Get("json")
		if tagVal != "" {
			fmt.Printf("field %d: tag为=%v\n", i, tagVal)
		}
	}

	numOfMethod := val.Elem().NumMethod()
	fmt.Printf("struct has %d methods \n", numOfMethod)

	// 方法的排序是默认按照 函数名排序(ASCII码)
	val.Elem().Method(1).Call(nil) // 获取到第二个方法 调用它

	// 调用结构体第1个方法
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Elem().Method(0).Call(params)
	fmt.Println("res=", res[0].Int()) // 返回的结果是 []reflect.Value
}

func main() {
	var a Monster = Monster{
		Name:  "黄鼠狼",
		Age:   400,
		Score: 30.1,
		Sex:   "男",
	}

	TestStruct(&a)
}

运行的结果为:

go
// struct has 4 fields
// field 0: 值为=嘿嘿嘿
// field 0: tag为=name
// field 1: 值为=400
// field 1: tag为=monster_age
// field 2: 值为=30.1
// field 3: 值为=男
// struct has 3 methods
// ---- start ----
// {嘿嘿嘿 400 30.1 男}
// ---- end ----
// res= 50

JSON 编码器

简化版的 JSON 编码器实现。

go
package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string
	Age  int
}

func ToJSON(v interface{}) (string, error) {
	var result string
	val := reflect.ValueOf(v)

	switch val.Kind() {
	case reflect.Struct:
		result += "{"
		for i := 0; i < val.NumField(); i++ {
			field := val.Type().Field(i)
			fieldValue := val.Field(i)

			if i > 0 {
				result += ","
			}
			result += fmt.Sprintf("\"%s\":", field.Name)

			switch fieldValue.Kind() {
			case reflect.String:
				result += fmt.Sprintf("\"%v\"", fieldValue.Interface())
			case reflect.Int, reflect.Float64:
				result += fmt.Sprintf("%v", fieldValue.Interface())
			default:
				subJson, _ := ToJSON(fieldValue.Interface())
				result += subJson
			}
		}
		result += "}"
	case reflect.Slice, reflect.Array:
		result += "["
		for i := 0; i < val.Len(); i++ {
			if i > 0 {
				result += ","
			}
			subJson, _ := ToJSON(val.Index(i).Interface())
			result += subJson
		}
		result += "]"
	default:
		return "", fmt.Errorf("unsupported type: %s", val.Kind())
	}

	return result, nil
}

func main() {
	p := Person{"Bob", 25}
	jsonStr, _ := ToJSON(p)
	fmt.Println(jsonStr) // {"Name":"Bob","Age":25}
}

适配器函数

go
package main

import (
	"fmt"
	"reflect"
)

func main() {

	call1 := func(v1, v2 int) {
		fmt.Printf("v1 = %v v2=%v \n", v1, v2)
	}

	call2 := func(v1 int, v2 int, s string) {
		fmt.Printf("v1 = %v v2=%v s=%v \n", v1, v2, s)
	}

	var (
		function reflect.Value
		inValue  []reflect.Value
		n        int
	)

	bridge := func(call interface{}, args ...interface{}) {
		n = len(args)
		inValue = make([]reflect.Value, n)
		for i := 0; i < n; i++ {
			inValue[i] = reflect.ValueOf(args[i])
		}
		function = reflect.ValueOf(call)
		function.Call(inValue)
	}

	bridge(call1, 1, 2)          // v1 = 1 v2=2
	bridge(call2, 1, 2, "hello") // v1 = 1 v2=2 s=hello
}

操作任意结构体

go
package main

import (
	"fmt"
	"reflect"
)

type user struct {
	UserId string
	Name   string
}

func main() {
	var (
		model *user
		sv    reflect.Value
	)

	model = &user{}
	sv = reflect.ValueOf(model)

	sv = sv.Elem()

	sv.FieldByName("UserId").SetString("6666")
	sv.FieldByName("Name").SetString("wanglei-shuaige")

	fmt.Println(*model) // {6666 wanglei-shuaige}
}

创建并操作结构体

go
package main

import (
	"fmt"
	"reflect"
)

type user struct {
	UserId string
	Name   string
}

func main() {
	var (
		model *user
		st    reflect.Type
		elem  reflect.Value
	)

	st = reflect.TypeOf(model)
	fmt.Println("reflect.TypeOf", st.Kind().String()) // reflect.TypeOf ptr

	st = st.Elem()
	fmt.Println("reflect.TypeOf.Elem", st.Kind().String()) // reflect.TypeOf.Elem struct

	// New 返回一个 Value 类型值,该值持有一个指向类型为 type 的新申请的零值的指针
	elem = reflect.New(st)

	fmt.Println("reflect.New", elem.Kind().String())             // reflect.New ptr
	fmt.Println("reflect.New.Elem", elem.Elem().Kind().String()) // reflect.New.Elem struct

	// model 就是创建的 user 结构体的实例
	model = elem.Interface().(*user) // model 是 *user 它的指向和 elem 是一样的

	elem = elem.Elem() // 取得 elem 指向的值

	elem.FieldByName("UserId").SetString("6666")
	elem.FieldByName("Name").SetString("wanglei-shuaige")

	fmt.Println(*model) // {6666 wanglei-shuaige}
}
#反射 #Golang