go 反射


反射

  • reflect
  • 反射可以在运行时动态获取变量/实例的各种信息,例如:变量的类型;
  • 如果是结构体变量,反射还可以获取到结构体本身的信息(结构体的字段、方法等);
  • 通过反射可以修改变量的值,可以调用关联的方法;

反射的相关函数

  • relect.TypeOf(变量名): 获取变量的类型,返回 reflect.Type 类型;
  • relect.ValueOf(变量名): 获取变量的值,返回 reflect.Value 类型; reflect.Value是一个结构体类型,通过 reflect.Value 可以获取到关于该变量的很多信息;
  • reflect.Value 需要调用具体类型的方法, 获取转换类型后的值,例如:val.Int();具体类型使用断言判断;
  • func (v Value) Interface() (i interface{}) : 此函数将 reflect.Value 转换成空接口;借助断言判断并转换成真实类型;
type Type
    func TypeOf(i interface{}) Type
    func PtrTo(t Type) Type
    func SliceOf(t Type) Type
    func MapOf(key, elem Type) Type
    func ChanOf(dir ChanDir, t Type) Type
type Value
    func ValueOf(i interface{}) Value
    ...

基本数据类型的反射

  • 借助空接口 进行转换;
  • 使用reflect.Value.Int() 转换成真实类型;
  • 使用reflect.Value.Interface() + 断言,判断类型并转换成真实类型,进行后续操作;

反射设置变量值

  • 需要传入地址;
  • 使用Elem()reVal.Elem().SetInt(19);

结构体类型的反射

  • 结构体反射使用空接口即可,别的操作类似;

反射操作结构体的属性和方法

  • field := val.NumField();/val.Field(i), 输出:NumField 字段个数;Field类似于java 的字段数组;
  • 字段修改,传参时需要传入指针;此时操作Field 需要加 reflect.ValueOf().Elem().Field(0), Elem 相当于解地址;
  • 字段修改使用:val.Elem().Field(0).SetString("xxx")/ SetInt(18) , 字段也按结构体的定义先后排序;
  • 方法调用:val.Method(2).Call(nil); val.MethodByName("Print").Call(nil); 方法首字母必须大写才有对应的反射的访问权限;
  • 方法顺序 按照 ASCII 码的顺序排列,使用 val.Method(2).Call(nil) 时传入的2/索引值,需要用到;
  • 调用带参数方法,入参要使用[]reflect.Value + append 构造切片;具体看示例代码;

反射获取变量的类别

  • reflect.Type.Kind()
  • reflect.Value.Kind():
  • Kind:
type Kind uint
Kind代表Type类型值表示的具体分类。零值表示非法分类。

const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Ptr
    Slice
    String
    Struct
    UnsafePointer
)

示例代码

package main

import (
	"fmt"
	"reflect"
)

// 函数参数定义为空接口,可接收任何类型的数据
func baseTypeReflect(i interface{})  {

	reType := reflect.TypeOf(i)
	fmt.Println("type(reflect.Type 类型)=", reType)
	val := reflect.ValueOf(i)
	fmt.Println("value(reflect.Value 类型)=", val)

	num2 := 10 + val.Int()
	fmt.Println("num2=", num2)

	fmt.Println("==>> reflect.Value 转换成空接口,再借助断言转换+判断:")
	valIface := val.Interface()
	valInt, ok := valIface.(int)
	if ok {
		n2 := valInt +1
		fmt.Println("(valInt )=", valInt)
		fmt.Println("(valInt + 1)=", n2)
	} else {
		fmt.Println("val 不是int类型...")
	}
}


type Student struct {
	Name string
	Age int
}

func structReflect(i interface{}) {
	reType := reflect.TypeOf(i)
	fmt.Println("type(reflect.Type 类型)=", reType)
	fmt.Printf("type 的真实类型(go类型)=%T \n", reType)
	val := reflect.ValueOf(i)
	fmt.Println("value(reflect.Value 类型)=", val)
	fmt.Printf("value 的真实类型(go类型)=%T \n", val)

	fmt.Println("==>> reflect.Value 转换成空接口,再借助断言转换+判断:")
	valIface := val.Interface()
	valStu, ok := valIface.(Student)
	if ok {
		fmt.Println("(student )=", valStu)
	} else {
		fmt.Println("val 不是student类型...")
	}
}

func kindReflect(i interface{}) {
	reType := reflect.TypeOf(i)
	val := reflect.ValueOf(i)

	// 获取变量类别: struct
	kind := reType.Kind()
	fmt.Println("reflect.TypeOf(i).kind=", kind) // struct

	valKind := val.Kind()
	fmt.Println("reflect.ValueOf(i).kind=", valKind) // struct

	valIface := val.Interface()
	valStu, ok := valIface.(Student)
	if ok {
		fmt.Printf("入参的类型是:%T \n", valStu)  // main.Student
	} else {
		fmt.Println("val 不是student类型...")
	}
}

func altVar(i interface{}){
	reVal := reflect.ValueOf(i)
	reVal.Elem().SetInt(18)
}

func (s Student) Print()  {
	fmt.Println("call_func: Print()")
	fmt.Println("学生的名字是:", s.Name)
}
func (s Student) GetSum(n1, n2 int) int {
	return n1 + n2
}
func (s Student) Set(name string, age int) {
	s.Name = name
	s.Age = age
}

// 反射查询结构体的字段
func getStructField(i interface{}){
	val := reflect.ValueOf(i)
	fmt.Println("val=", val)

	// 通过 reflect.Value 类型操作结构体内部的字段:
	field := val.NumField()
	fmt.Println("NumField=", field)
	for i := 0; i < field; i++ {
		fmt.Printf("第 %d 个字段的值是: %v \n", i, val.Field(i))
	}
}
// 反射调用结构体的方法
func callStructMethod(i interface{}){
	val := reflect.ValueOf(i)
	fmt.Println("val=", val)

	numMethod := val.NumMethod()
	fmt.Println("numMethod=", numMethod)
	// 调用
	fmt.Println("val.Method(1)=", val.MethodByName("Print"))
	val.MethodByName("Print").Call(nil)
	val.Method(1).Call(nil)

	fmt.Println("==>> 调用带参数的 GetSum 方法:")
	var params []reflect.Value
	params = append(params, reflect.ValueOf(1))
	params = append(params, reflect.ValueOf(2))
	sum := val.Method(0).Call(params)
	fmt.Println("sum=", sum[0])

}

// 反射操作结构体的字段
func altStructField(i interface{}){
	val := reflect.ValueOf(i)
	fmt.Println("val=", val)

	// 通过 reflect.Value 类型操作结构体内部的字段:
	field := val.Elem().NumField()
	fmt.Println("NumField=", field)
	for i := 0; i < field; i++ {
		fmt.Printf("第 %d 个字段的值是: %v \n", i, val.Elem().Field(i))
	}

	fmt.Println("==>> 修改结构体的字段值:(脑斧 ->小脑斧 -> 小脑斧~)")
	val.Elem().Field(0).SetString("小脑斧")
	val.Elem().FieldByName("Name").SetString("小脑斧~")

}

func main() {
	fmt.Println("==>> 反射:基本数据类型:")
	var num int = 10
	baseTypeReflect(num)

	fmt.Println("\n==>> 反射:对结构体进行反射:")
	student := Student{Name: "脑斧", Age: 18}
	structReflect(student)

	fmt.Println("\n==>> 反射获取变量类别(kind):")
	kindReflect(student)
	kindReflect(&num) // ptr
	kindReflect(num)  // int

	fmt.Println("\n==>> 反射设置变量值:")
	fmt.Println("num 反射修改前值为:", num)
	altVar(&num)
	fmt.Println("num 利用反射修改后值为:", num)

	fmt.Println("\n==>> 反射操作结构体的属性和方法:")
	fmt.Println("==>> 反射查询结构体的属性:")
	getStructField(student)
	fmt.Println("==>> 反射调用结构体的方法:")
	callStructMethod(student)
	fmt.Println("\n==>> 反射修改结构体字段的值:")
	altStructField(&student)
	fmt.Println("student=", student)
}

运行结果

==>> 反射:基本数据类型:
type(reflect.Type 类型)= int
value(reflect.Value 类型)= 10
num2= 20
==>> reflect.Value 转换成空接口,再借助断言转换+判断:
(valInt )= 10
(valInt + 1)= 11

==>> 反射:对结构体进行反射:
type(reflect.Type 类型)= main.Student
type 的真实类型(go类型)=*reflect.rtype 
value(reflect.Value 类型)= {脑斧 18}
value 的真实类型(go类型)=reflect.Value 
==>> reflect.Value 转换成空接口,再借助断言转换+判断:
(student )= {脑斧 18}

==>> 反射获取变量类别(kind):
reflect.TypeOf(i).kind= struct
reflect.ValueOf(i).kind= struct
入参的类型是:main.Student 
reflect.TypeOf(i).kind= ptr
reflect.ValueOf(i).kind= ptr
val 不是student类型...
reflect.TypeOf(i).kind= int
reflect.ValueOf(i).kind= int
val 不是student类型...

==>> 反射设置变量值:
num 反射修改前值为: 10
num 利用反射修改后值为: 18

==>> 反射操作结构体的属性和方法:
==>> 反射查询结构体的属性:
val= {脑斧 18}
NumField= 20 个字段的值是: 脑斧 
第 1 个字段的值是: 18 
==>> 反射调用结构体的方法:
val= {脑斧 18}
numMethod= 3
val.Method(1)= 0x649020
call_func: Print()
学生的名字是: 脑斧
call_func: Print()
学生的名字是: 脑斧
==>> 调用带参数的 GetSum 方法:
sum= 3

==>> 反射修改结构体字段的值:
val= &{脑斧 18}
NumField= 20 个字段的值是: 脑斧 
第 1 个字段的值是: 18 
==>> 修改结构体的字段值:(脑斧 ->小脑斧 -> 小脑斧~)
student= {小脑斧~ 18}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值