跳至主要內容

反射 (relect包)

LincZero大约 3 分钟

反射 (relect包)

核心:relect包

概念

Go反射可以做什么?

  • 运行时动态获取:变量的各种信息
    • 普通变量的 类型、类别等
    • 结构体的 字段、方法
  • 运行时动态修改和调用
    • 修改变量的值
    • 调用关联的方法

相关函数

// `reflect包.TypeOf(变量名)`,获取变量的类型,返回reflect.Type类型
func TypeOf(i interface{}) Type

// `reflectb包.ValueOf(变量名)`,获取变量的值,返回reflect.Value类型。通过该类型可以获取该变量很多信息
func ValueOf(i interface{}) Value

两个主要方法和类型

TypeOf

见下,代码连在一起了

ValueOf

package main

import(
	"fmt"
    "reflect"
)

func testReflect(i interface{})	{ 		// 100 (`int` -> `interface{}`, 空接口)
    // TypeOf
    reType := reflect.TypeOf(i)
    fmt.Println("reType:", reType)		// int
    
    // ValueOf
    reValue2 := reflect.ValueOf(i)		// 100 (`interface{}` -> `reflect.Value`)
    fmt.Println("reValue:", reValue2)
}

type Student struct{
    Name string
    Age int
}

func main() {
    // 基本类型
	var num int = 100
    testReflect(num)
    
    // 结构体类型
    student := Student {
        Name: "Lili",
        Age: 18,
    }
    testReflect(student)
}

reflect.Type

获取变量的类别

获取变量类别有两种方式:

k1 := reType.Kind()
fmt.Println(k1)		// struct

k2 := reValue.Kind()
fmt.Println(k2)		// struct

注意区分:类别类型

Kind方法只能获取到是struct (类别),并无法获取到类名 (类型)

reflect.Value

类型互转

基本类型

基本、interface{}、reflect.Value 三者互转

num1 := 100

var inter1 interface{} = num1		// 100 (`int` 			-> `interface{}`)
num2, _ := inter2.(int)				// 100 (`interface{}`	-> `int`) 类型断言

reValue2 := reflect.ValueOf(num1)	// 100 (`int`			-> `reflect.Value`)
reValue := reflect.ValueOf(inter1)	// 100 (`interface{}`	-> `reflect.Value`)

num3 := reValue.Int()				// 100 (`reflect.Value`	-> `int`)
inter2 := reValue.Interface() 		// 100 (`reflect.Value`	-> `interface{}`)

结构体

结构体、interface{}、reflect.Value 三者互转

student := Student {
    Name: "Lili",
    Age: 18,
}

var inter1 interface{} = stu1		// Lili 18 (`XStruct` 		-> `interface{}`)
stu2, flag := inter2.(Student)		// Lili 18 (`interface{}`	-> `XStruct`) 类型断言,需要判断flag

reValue2 := reflect.ValueOf(stu1)	// Lili 18 (`XStruct`		-> `reflect.Value`)
reValue := reflect.ValueOf(inter1)	// Lili 18 (`interface{}`	-> `reflect.Value`)

stu3, _ := rV.Interface().(Student)	// Lili 18 (`reflect.Value`	-> `XStruct`) 这里无法直接转换,需要先转空接口再用类型断言
inter2 := reValue.Interface() 		// Lili 18 (`reflect.Value`	-> `interface{}`)

修改变量的值

基本类型 (.Elem、.setInt)

需要用到一个接口

// 原型
// @param v Elem方法会返回v保管值的Value封装,或v持有指针指向值的Value封装。如果v的Kind不是Interface或Ptr会Panic
// @return 如果v持有值为nil,会返回Value零值
func (v Value) Elem() Value

首先这里修改成传递地址

// 变量准备
var i1 = 100
var inter1 interface{} = &i1

// 修改值
reValue := reflect.ValueOf(inter1)		// 对应 `*int`
reValue2 := reValue.Elem()				// 对应 `int引用`
reValue2.SetInt(40)						// 修改值

fmt.println(i1)							// 40

结构体 (多了个Field()方法)

// 变量准备
type Student struct{
    Name string
    Age int
}
s := Student{
    Name: "Lili"
    Age: 18
}
var inter1 interface{} = &s

// 修改值
reValue := reflect.ValueOf(inter1)		// 对应 `*Student`
reValue2 := reValue.Elem()				// 对应 `Student引用`
//n := reValue2.NumField()				// 字段数。2
reValue2.Field(0).SetString("LiSi")		// 修改值

操作结构体的字段和方法

package main

import(
	"fmt"
    "reflect"
)

// 类
type Student struct{
    Name string
    Age int
}
func (s Student) CPrint() {
    fmt.Println("调用了Print方法")
    fmt.Println("学生名:", s.Name)
}
func (s Student) AGetSum(n1, n2 int) int {
    return n1+n2
}
func (s Student) BSet(name string, age int) int {
	s.Name = name
    s.Age = age
}

// 操作结构体进行反射
func TestStudentStruct(a interface{}){
    reV := reflect.ValueOf(a)			// `interface{}` -> `reflect.Value`
    
    // 结构体字段
    n1 := reV.NumField()				// 获取结构体字段数量。打印:2
    for i:=0; i<n1; i++{				// 获取结构体字段内容
        fmt.Printf("第%d个字段的值为:%v\n", i, val.Field(i))
    }
    
    // 结构体方法
    n2 := reV.NumMethod()				// 获取结构体方法数量。打印:3
    vaV.Method(2).Call(nil)				// 调用结构体方法。 (需要注意,这玩意居然是按字母而非定义顺序排序的。这里调用的是CPrint方法)
}

// 主函数
func main() {
    s := Student{
        Name: "Lili"
        Age: 18
    }
    TestStudentStruct(s)
}