go学习之反射知识

反射

文章目录

    • 反射
      • 1、反射的使用场景
        • 1)结构体标签的应用
        • 2)使用反射机制编写函数的适配器(桥连接)
      • 2、反射的基本介绍
        • -1.基本介绍
        • -2.反射的图解
        • -3.反射重要的函数和概念
      • 3.反射快速入门
        • -1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。
        • -2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
      • 4.反射的注意事项和细节说明
        • 1)reflect.Value.Kind,获取变量的类别,返回的是一个常量
        • 2)Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的
      • 5.反射练习题
      • 6.反射的最佳实践

1、反射的使用场景

1)结构体标签的应用

go学习之反射知识_第1张图片

2)使用反射机制编写函数的适配器(桥连接)

go学习之反射知识_第2张图片

2、反射的基本介绍

-1.基本介绍

1)反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)

2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)

3)通过反射,可以修改变量的值,可以调用关联的方法

4)使用反射,需要import (“reflect”)

-2.反射的图解

go学习之反射知识_第3张图片

-3.反射重要的函数和概念

1)reflect.TypeOf(变量名)。获取变量的类型,返回reflect.Type类型

2)reflect.ValueOf(变量名)。获取变量的值,返回reflect.Value类型reflect.Value是一个结构体类型。通过reflect.Value.可以获取到关于该变量的很多信息

3)变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。

3.反射快速入门

-1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。

代码演示

package main
 import (
	"fmt"
	"reflect"
 )

 //专门反射演示
 func reflectTest01(b interface{}) {
	//通过反射获取的传入的变量 type,kind,值
	//1.先获取到reflect.Type
	rTyp :=reflect.TypeOf(b)
	fmt.Println("rTyp=",rTyp) //int
	//2.获取到reflectValue
	rVal :=reflect.ValueOf(b) //100
	// n1 := 10
	// n2 := 2 + rVal
	// fmt.Println("n2=",n2)//erro 

	// n1 := 10
	n2 := 2 + rVal.Int()
	fmt.Println("n2=",n2) //102

	fmt.Printf("rVal=%v type=%T\n",rVal,rVal) //rVal=100 type=reflect.Value

	//下面我们将rVal转成interface{}
	iv := rVal.Interface()
	//将interface{}通过断言转成需要的类型
	num2 := iv.(int)
	fmt.Println("num2=",num2) //num2= 100
 }
 func main() {
	//-1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。
	
	//1、先定义一个int
	var num int = 100
	reflectTest01(num)
 }
-2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
 //专门演示对结构体的反射
 func reflectTest02(b interface{}) {
	//通过反射获取的传入的变量 type,kind,值
	//1.先获取到reflect.Type
	rTyp :=reflect.TypeOf(b)
	fmt.Println("rTyp=",rTyp) //rTyp= main.Student

	//2.获取到reflectValue
	rVal :=reflect.ValueOf(b)
	
	//下面我们将rVal转成interface{}
	iv := rVal.Interface()
	fmt.Printf("iv=%v ,iv type=%T\n",iv,iv) //iv={Tom 20} ,iv type=main.Student
	//将interface{}通过断言转成需要的类型
	// fmt.Printf("iv=%v ,iv type=%T name\n",iv,iv,iv.Name) //erro无法取出name的值
	//所以先要进行断言
	stu,ok :=iv.(Student)
	if ok {
		fmt.Printf("stu.Name=%v\n",stu.Name)//stu.Name=Tom
	}

	
 }

 type Student struct {
	Name string
	Age int
 }
 func main() {


	//2.定义一个Student的实例
	stu := Student{
		Name : "Tom",
		Age : 20,
	}

	reflectTest02(stu)

 }

4.反射的注意事项和细节说明

1)reflect.Value.Kind,获取变量的类别,返回的是一个常量

补充常量是知识

常量介绍

  • 常量使用const修改

  • 常量在定义的时候,必须初始化

  • 常量不能修改

  • 常量只能修饰bool、数值类型(int,float系列)、string类型

    语法: const identifier [type] = value

    package main
    import (
    	"fmt"
    )
    func main() {
    	var num int
    	num = 90 
    	//常量声明的时候必须赋值
    	const tax int = 90
    	// tax = 10 //常量是不能修改的
    	fmt.Println(num,tax)
        //常量只能修饰bool、数值类型(int,float系列)、string类型
        //const b =num /3 erro
    }
    

    常量使用注意事项

    1》比较简洁的写法

    func main () {
    const(
    	a = 1
    	b = 2
    )
    }
    

    2》还有一种专业的写法

    func main(){
    	const(
    	a = iota
        b
        c
    	)
    	fmt.Println(a,b,c)//0,1,2
    }
    

    3》Golang中没有常量名必须大写的规范

    4》仍然通过首字母的大小来控制常量的访问范围

2)Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的

比如: var num int = 10 num 的Type是int, Kind也是int

比如:var stu Student stu 的Type是包名.Student,Kind是struct(Kind的等级 >Type的等级)

3)通过反射可以让变量在interface{}和Reflect.Value之间相互转换

4)使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x)Int(),而不能使用其他的,否则报panic

5)通过反射的值来修改变量。注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的值,同时需要使用到reflect.Value.Elem()方法

package main
import (
	"fmt"
	"reflect"
)

//通过反射,修改
//num int 的值
//修改student的值

func reflect01(b interface{}){
	//获取到reflect.Value
	rVal := reflect.ValueOf(b)
	rVal.Elem().SetInt(20)
}
func main() {
	var num int = 10
	reflect01(&num)
	fmt.Println("num=",num) //20


	//你可以这样理解这句话:rVal.Elem()
	// num := 9
	//  ptr *int = &num
	//  num2 :=*ptr
}

6)如何理解rVal.Elem()

//你可以这样理解这句话:rVal.Elem()
	// num := 9
	//  ptr *int = &num
	//  num2 :=*ptr
}

5.反射练习题

1)给你一个变量 var v float64 = 1.2.请使用反射来得到它的reflect.Value,然后获取对应的Type,Kind和值,并将reflect.Value转换成interface{}.,再将interface{}转换成float64

package main
 import (
	"fmt"
	"reflect"
 )
 func reflect01(b interface{})  {
	num := reflect.ValueOf(b)
    kind1 :=num.Kind()
	iv :=num.Interface()
	fmt.Printf("b的reflect.Value是=%v,kind值为=%v,num转换为interface的值为=%v",num,kind1,iv)
 }
 func main() {
	var n float64 =65.9
	reflect01(n)
 }

2)给字符串改名题

        var str string = "tom"
        fs :=reflect.ValueOf(&str) //这里要改成地址
        fs.Elem().SetString("jackma")
        fmt.Println(str)

6.反射的最佳实践

1)使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
2)使用反射的方式来获取结构体的tag标签,遍历字段的值,修改字段值,调用结构体方法
val.Method(0).Call() 调用第一个方法
方法的排序默认是按照函数名的排序(ASCII码)
定义了两个函数testl 和test2, 定义一个适配器函数用作统一 处理接口
使用反射操作任意结构体类型
使用反射创建并操作结构体

package main
import (
	"fmt"
	"reflect"
)

//定义了一个Monster结构体
type Monster struct{
	Name string `json:"name"`
	Age int `json:"age"`
	Score float32 `json:"成绩"`
	Sex string
}

// 方法: 返回两数之和
func(s Monster) GetSum(n1, n2 int) int{
	return n1 + n2
}

// 方法: 接收四个值, 给S赋值
func(s Monster) Set(name string, age int, score float32, sex string){
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

//方法,显示s的值
func (s Monster) Print() {
	fmt.Println("--start--")
	fmt.Println(s)
	fmt.Println("--end--")
}

func TestStruct(a interface{}){
	//获取reflect.Type类型
	typ := reflect.TypeOf(a)
	//获取reflect.Value类型
	val := reflect.ValueOf(a)
	//获取到a对应的类别
	kd := val.Kind()
	//如果传入的不是struct,就退出
	if kd != reflect.Struct {
		fmt.Println("Expect struct")
		return
	}
	//获取到该结构体有几个字段
	num := val.NumField()
	fmt.Printf("struct has %d fields\n", num) //4
	//变量结构体的所有字段
	for i := 0; i < num; i++ {
		fmt.Printf("Field %d:值为=%v\n", i, val.Field(i))
		//获取到struct 标签,注意需要通过reflect.Type来获取tag标签的值
		tagVal := typ.Field(i).Tag.Get("json")
		//如果该字段于tag标签就显示,否则就不显示
		if tagVal != "" {
			fmt.Printf("Field %d: tag为=%v\n", i, tagVal)
		}
	}
	//获取到该结构体有多少个方法
	numOfMethod := val.NumMethod()
	fmt.Printf("struct has %d methods\n", numOfMethod)
	//var params []reflect.Value
	//方法的排序默认是按照函数名的排序(ASCII码)
	val.Method(1).Call(nil)//获取到第二个方法。调用它
	
	//调用结构体的第1个方法Method(0)
	var params []reflect.Value //声明了[]reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Method(0).Call(params) //传入的参数是[]reflect.Value,返回[]reflect.Value
	fmt.Println("res=", res[0].Int()) //返回结果,返回的结果是[]reflect.Value*/
}

func main(){
	var a Monster = Monster{
		Name: "黄鼠狼精",
		Age: 400,
		Score: 30.8,
	} 
	//将Monster实例传递给TestStruct函数
	TestStruct(a)
}

2)使用反射操作任意结构体类型

type user struct {
	UserId string
	Name string
 }

 func TestReflectStruct(t *testing.T){
	var {
		model *user
		sv reflect.Value
	}
	model = &user{}
	sv = reflect.ValueOf(model)
	t.Log("reflect.ValueOf",sv.kind().String())
	sv=sv.Elem()
	t.Log("reflect.ValueOf",,sv.kind().String())
	sv.FieldByName("userId").SetString("12345678")
	sv.FieldByName("Name").SetString("nickname")
	t.Log("model",model)
 }

你可能感兴趣的:(学习日记,golang学习,golang,学习)