• Go 反射


    获取类型和值

    • 之前讲过接口nil不一定等于空接口,因为一个 interface 底层 由 type + value 构成,只有 typevalue 都匹配,才能 ==
    • reflect.VlaueOf 就是用来获取具体的 reflect.Value
    • reflect.TypeOf 用来获取具体的 reflect.Type
    func main() {
    	var (
    		a *A
    		b interface{}
    	)
    	fmt.Println(a)
    	if b == nil {
    		fmt.Println("b is nil")
    	}
    	fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))
    	fmt.Println(reflect.TypeOf(a), reflect.ValueOf(a))
    	b = a
    	if b == nil {
    		fmt.Println("b is nil")
    	} else {
    		fmt.Printf("current b is %v \n", b)
    		fmt.Println("b not eq nil")
    	}
    	fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    image.png
    上面的代码说明了,刚开始的空接口 == nil,后来的接口为啥不等于 nil,因为 type变了,虽然value 还是 nil

    获取属性的类型和值

    • 通过 reflect.Value 或者 reflect.TypeNumField 获取属性数量
    • 通过 reflect.TypeField 方法 获取属性相关信息
    • 通过 reflect.ValueField 方法 获取值相关信息
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type A struct {
    	Name string
    	Age  int
    }
    
    func main() {
    	var a A
    	getType := reflect.TypeOf(a)
    	getValue := reflect.ValueOf(a)
    	fmt.Println("field num", getType.NumField())
    	for i := 0; i < getType.NumField(); i++ {
    		field := getType.Field(i)
    		value := getValue.Field(i)
    		fmt.Println("field name is", field.Name, "field value is", value.Interface())
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    image.png

    通过反射修改值

    • 通过获取value的反射对象即可,reflect.ValueOf 传入的必须是指针类型,只有原始反射对象可以进行修改,可以通过 reflect.ValueElem 方法取得
    • 通过 reflect.ValueCanset 方法来判断是否可以设置
    • 通过 Set... 系列方法来设置具体类型的值
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type A struct {
    	Name string
    	Age  int
    }
    
    func main() {
    	a := A{
    		Name: "old name",
    	}
    
    	valueOfA := reflect.ValueOf(&a).Elem()
    
    	nameField := valueOfA.Field(0)
    
    	if nameField.CanSet() {
    		nameField.SetString("new name")
    	} else {
    		fmt.Println("don't set")
    	}
    	
    	fmt.Println("new value", a.Name)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 因为调用 set... 设置值,需要知道类型,可以通过 reflect.Typekind 方法获取原始类型
      • 再通过 switch 去匹配类型来调用具体的 set... 方法
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type A struct {
    	Name string
    	Age  int
    }
    
    func main() {
    	a := A{
    		Name: "old name",
    	}
    
    	fmt.Println("old value", a.Name)
    	valueOfA := reflect.ValueOf(&a).Elem()
    	getType := reflect.TypeOf(a)
    	field := getType.Field(0)
    
    	nameField := valueOfA.Field(0)
    
    	if nameField.CanSet() {
    		switch field.Type.Kind() {
    		case reflect.String:
    			fmt.Println("string")
    			nameField.SetString("new value")
    		}
    	} else {
    		fmt.Println("don't set")
    	}
    
    	fmt.Println("new value", a.Name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    image.png

    获取方法的名称和类型

    • 先通过 reflect.TypeNumMethod 方法获取方法数量
    • 在通过 reflect.TypeMethod 方法获取到具体的方法信息 reflect.Method
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type A struct {
    	Name string
    	Age  int
    }
    
    func (receiver *A) SetName(name string) {
    	receiver.Name = name
    }
    
    func (receiver *A) SetAge(age int) {
    	receiver.Age = age
    }
    
    func main() {
    	var a A
    	//有方法是依赖指针的所以需要传指针
    	getType := reflect.TypeOf(&a)
    	num := getType.NumMethod()
    	for i := 0; i < num; i++ {
    		method := getType.Method(i)
    		fmt.Println("method name:", method.Name, "method type:", method.Type)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    image.png

    调用方法

    • 通过 reflect.MethodCall 方法即可调用反射对象的方法
      • Call 中 接收的参数为 reflect.Value 的切片
      • 如果反射对象的方法不需要参数,传一个 reflect.Value 的空切片即可
      • 如果反射对象需参数,那么需要由反射对像参数的 reflect.Value 组成切片,传入 Call 完成调用
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type A struct {
    	Name string
    	Age  int
    }
    
    type Body struct {
    	Like string
    	Desc string
    }
    
    func (a A) Pr() {
    	fmt.Println("A pr")
    }
    
    func (a A) Talk(b Body) {
    	fmt.Printf("Like:%s,Desc:%s", b.Like, b.Desc)
    }
    
    func main() {
    	var a A
    	getType := reflect.ValueOf(a)
    	pr := getType.Method(0)
    	//不需要参数
    	pr.Call([]reflect.Value{})
    	b := Body{
    		Like: "i'm like",
    		Desc: "i'm desc",
    	}
    	talk := getType.Method(1)
    	//Talk 需要传入 Body struct, 所以反射调用,需要传入 由 Body的 reflect.Value 组成切片参数
    	talk.Call([]reflect.Value{
    		reflect.ValueOf(b),
    	})
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    image.png

    反射的缺点

    • 反射慢
      • 不管什么编程语言,反射都慢
      • 反射实现里有对 reflect.kind 大量的枚举 + 类型转换 等操作
      • reflect.Value 不能复用,每次都是返回一个新的值,其中 typ 还是指针类型,涉及对指针的频繁分配,GC

    image.png
    image.png
    image.png

  • 相关阅读:
    基于小波分析与深度学习的脑电信号分类(matlab)
    NNDL 作业8:RNN - 简单循环网络
    【JVM 系列】JVM 对象的分配策略
    MySQL笔记(进阶篇)
    人工智能给我们的生活带来了巨大的影响?
    web前端期末大作业:基于html化妆品购物商城项目的设计与实现——化妆品官方网站设计与实现(HTML+CSS+JS)
    【Visual Leak Detector】源码编译 VLD 库
    浅谈——网络安全架构设计(三)
    峰会实录 | StarRocks PMC Chair 赵纯:数据分析的极速统一3.0 时代
    Python in Visual Studio Code 2023年10月发布
  • 原文地址:https://blog.csdn.net/qq_29744347/article/details/134061940