【Go】二十、反射

水深无声 2024-04-23 15:45 129阅读 0赞

文章目录

  • 1、反射
  • 2、对基本数据类型反射
  • 3、对结构体进行反射
  • 4、获取变量的类别
  • 5、通过反射修改基本类型变量的值
  • 6、通过反射操作结构体的属性和方法

1、反射

  1. //核心包
  2. import ("reflect")

通过反射:

  • 可以在运行时动态获取变量的类型、获取结构体的信息(字段、方法)
  • 可以修改变量的值,调用关联的方法

相关函数:

  1. //作用:反射获取变量的类型
  2. //返回类型是reflect.Type类型
  3. reflect.TypeOf(变量名)
  4. //作用:反射获取变量的值
  5. //返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息
  6. reflect.ValueOf(变量名)

2、对基本数据类型反射

空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为可以把任何一个变量赋给空接口。有点像Object类

  1. package main
  2. import(
  3. "fmt"
  4. "reflect"
  5. )
  6. //利用一个函数,函数的参数定义为空接口:
  7. func testReflect(i interface{
  8. }){
  9. //1.调用TypeOf函数,返回reflect.Type类型数据:
  10. reType := reflect.TypeOf(i) //拿类型
  11. fmt.Println("reType:",reType)
  12. fmt.Printf("reType的具体类型是:%T",reType)
  13. fmt.Println()
  14. //2.调用ValueOf函数,返回reflect.Value类型数据:
  15. reValue :=reflect.ValueOf(i)
  16. fmt.Println("reValue:",reValue) //拿值
  17. fmt.Printf("reValue的具体类型是:%T",reValue)
  18. fmt.Println()
  19. //如果真想获取reValue的数值,要调用Int()方法:返回v持有的有符号整数
  20. num := reValue.Int()
  21. fmt.Printf("Int()后的类型是:%T",num)
  22. fmt.Println()
  23. //reflect.Value类型数据,转回去,可先将reValue转成空接口,再用断言转型:
  24. i2 := reValue.Interface()
  25. //类型断言:
  26. n := i2.(int)
  27. n2 := n + 30
  28. fmt.Println(n2)
  29. }
  30. func main(){
  31. //对基本数据类型进行反射:
  32. //定义一个基本数据类型:
  33. var num int = 100
  34. testReflect(num)
  35. }

运行:

在这里插入图片描述

注意点:如果有了reflect.Value类型,想转回原来的类型,可以用reflect.Value类型的Interface方法,转回空接口类型,再断言转型,回到int类型

在这里插入图片描述

3、对结构体进行反射

  1. package main
  2. import(
  3. "fmt"
  4. "reflect"
  5. )
  6. //利用一个函数,函数的参数定义为空接口:
  7. func testReflect(i interface{
  8. }){
  9. //1.调用TypeOf函数,返回reflect.Type类型数据:
  10. reType := reflect.TypeOf(i)
  11. fmt.Println("reType:",reType)
  12. fmt.Printf("reType的具体类型是:%T",reType)
  13. fmt.Println()
  14. //2.调用ValueOf函数,返回reflect.Value类型数据:
  15. reValue :=reflect.ValueOf(i)
  16. fmt.Println("reValue:",reValue)
  17. fmt.Printf("reValue的具体类型是:%T",reValue)
  18. fmt.Println()
  19. //reValue转成空接口:
  20. i2 := reValue.Interface()
  21. //类型断言:
  22. n,flag := i2.(Student)
  23. if flag == true {
  24. //断言成功
  25. fmt.Printf("学生的名字是:%v,学生的年龄是:%v",n.Name,n.Age)
  26. }
  27. }
  28. //定义学生结构体:
  29. type Student struct{
  30. Name string
  31. Age int
  32. }
  33. func main(){
  34. //对结构体类型进行反射:
  35. //定义结构体具体的实例:
  36. stu := Student{
  37. Name : "丽丽",
  38. Age : 18,
  39. }
  40. //反射
  41. testReflect(stu)
  42. }

运行:

在这里插入图片描述

4、获取变量的类别

在上面reflect.Type和reflect.Value类型对象的基础上,获取类别(Student是类型,其类别是struct结构体)。相关方法:

  1. reflect.Type.Kind()
  2. reflect.Value.Kind()

在这里插入图片描述

  1. package main
  2. import(
  3. "fmt"
  4. "reflect"
  5. )
  6. //利用一个函数,函数的参数定义为空接口:
  7. func testReflect(i interface{
  8. }){
  9. //空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
  10. //1.调用TypeOf函数,返回reflect.Type类型数据:
  11. reType := reflect.TypeOf(i)
  12. //2.调用ValueOf函数,返回reflect.Value类型数据:
  13. reValue :=reflect.ValueOf(i)
  14. //获取变量的类别:
  15. //(1)reType.Kind()
  16. k1 := reType.Kind()
  17. fmt.Println(k1)
  18. //(2)reValue.Kind()
  19. k2 := reValue.Kind()
  20. fmt.Println(k2)
  21. //获取变量的类型:
  22. //reValue转成空接口:
  23. i2 := reValue.Interface()
  24. //类型断言:
  25. n,flag := i2.(Student)
  26. if flag == true {
  27. //断言成功
  28. fmt.Printf("结构体的类型是:%T",n)
  29. }
  30. }
  31. //定义学生结构体:
  32. type Student struct{
  33. Name string
  34. Age int
  35. }
  36. func main(){
  37. //对结构体类型进行反射:
  38. //定义结构体具体的实例:
  39. stu := Student{
  40. Name : "丽丽",
  41. Age : 18,
  42. }
  43. testReflect(stu)
  44. }

运行:

在这里插入图片描述
类型Type和类别Kind的区别:

  1. var num int = 10 numTypeint , Kind也是int
  2. ar stu Studentstu Type pkg1.Student , Kindstruct

5、通过反射修改基本类型变量的值

调用reflect.Value的相关方法,SetInt、SetBoolean等等

  1. package main
  2. import(
  3. "fmt"
  4. "reflect"
  5. )
  6. //利用一个函数,函数的参数定义为空接口:
  7. func testReflect(i interface{
  8. }){
  9. //空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
  10. reValue :=reflect.ValueOf(i)
  11. //通过SetInt()来改变值:
  12. reValue.Elem().SetInt(40)
  13. }
  14. func main(){
  15. //对基本数据类型进行反射:
  16. //定义一个基本数据类型:
  17. var num int = 100
  18. testReflect(&num) //传入指针地址
  19. fmt.Println(num) //40
  20. }

注意,是改基本类型变量,值拷贝,要传入地址:

  1. testReflect(&num)

因为传入的是一个指针类型,想调用reflect.Value的相关方法,对上面的reValue 变量也得再转一下:

  1. reValue.Elem().SetInt(40)

在这里插入图片描述

6、通过反射操作结构体的属性和方法

和Java一样,获取所有的变量和所有的方法,调用方法,方法的首字母必须大写才能有对应的反射的访问权限。相关方法:

  • NumField:获取结构体字段的总数
  • Field:获取结构体的某一个字段,传i序号0、1、2
  • NumMethod:获取结构体中方法的数量
  • Method:获取结构体中的某一个方法,传i序号0、1、2
  • Call,反射调用方法,形参是一个切片,即调用方法的形参

    package main
    import(

    1. "fmt"
    2. "reflect"

    )
    //定义一个结构体:
    type Student struct{

    1. Name string
    2. Age int

    }
    //给结构体绑定方法:
    func (s Student) CPrint(){

    1. fmt.Println("调用了Print()方法")
    2. fmt.Println("学生的名字是:",s.Name)

    }
    func (s Student) AGetSum(n1,n2 int) int{

    1. fmt.Println("调用了AGetSum方法")
    2. return n1 + n2

    }
    func (s Student) BSet(name string,age int){

    1. s.Name = name
    2. s.Age = age

    }

  1. //定义函数操作结构体进行反射操作:
  2. func TestStudentStruct(a interface{
  3. }){
  4. //a转成reflect.Value类型:
  5. val := reflect.ValueOf(a)
  6. //通过reflect.Value类型操作结构体内部的字段个数:
  7. n1 := val.NumField()
  8. fmt.Println(n1) //2
  9. //遍历-获取具体的字段的值:
  10. for i := 0; i < n1;i++{
  11. fmt.Printf("第%d个字段的值是:%v",i,val.Field(i))
  12. }
  13. fmt.Println()
  14. //通过reflect.Value类型操作结构体内部的方法个数:
  15. n2 := val.NumMethod()
  16. fmt.Println(n2)
  17. //调用自定义结构体的CPrint()方法:
  18. //调用方法,方法的首字母必须大写才能有对应的反射的访问权限
  19. //方法的顺序按照ASCII的顺序排列的,a,b,c,,,,,,索引:0,1,2,,,,,
  20. val.Method(2).Call(nil) //nil给反射的那个方法传空参
  21. //调用AGetSum方法:
  22. //定义Value的切片:
  23. var params []reflect.Value
  24. params = append(params,reflect.ValueOf(10))
  25. params = append(params,reflect.ValueOf(20))
  26. result := val.Method(0).Call(params)
  27. fmt.Println("AGetSum方法的返回值为:",result[0].Int())
  28. }
  29. func main(){
  30. //定义结构体具体的实例:
  31. s := Student{
  32. Name : "丽丽",
  33. Age : 18,
  34. }
  35. //调用TestStudentStruct反射:
  36. TestStudentStruct(s)
  37. }

修改结构体属性的值:

  1. package main
  2. import(
  3. "fmt"
  4. "reflect"
  5. )
  6. //定义函数操作结构体进行反射操作:
  7. func TestStudentStruct(a interface{
  8. }){
  9. //a转成reflect.Value类型:
  10. val := reflect.ValueOf(a)
  11. fmt.Println(val)
  12. n := val.Elem().NumField()
  13. fmt.Println(n)
  14. //修改字段的值:
  15. val.Elem().Field(0).SetString("张三") 注意点2
  16. }
  17. func main(){
  18. //定义结构体具体的实例:
  19. s := Student{
  20. Name : "丽丽",
  21. Age : 18,
  22. }
  23. //调用TestStudentStruct:
  24. TestStudentStruct(&s) 注意点1
  25. fmt.Println(s)
  26. }

发表评论

表情:
评论列表 (有 0 条评论,129人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Go反射

    注意点:如果有了reflect.Value类型,想转回原来的类型,可以用reflect.Value类型的Interface方法,转回空接口类型,再断言转型,回到int类型...

    相关 Go 反射

    > [《Go 语言从入门到实战》][Go] 反射章节的学习笔记,欢迎阅读斧正。感觉该专栏整体来说对有些后端编程经验的来说比无后端编程经验的人更友好。 常用方法 1.