• Golang反射相关知识总结


    1. Golang反射概述

    Go语言的反射(reflection)是指在运行时动态地获取类型信息和操作对象的能力。在Go语言中,每个值都是一个接口类型,这个接口类型包含了这个值的类型信息和值的数据,因此,通过反射,可以获取一个值的类型信息,调用该值的方法,或者修改该值的属性等。
    Go语言中的反射机制主要通过reflect包来实现。reflect包提供了Type、Value和Kind三个类型,分别表示类型信息、值信息和值的种类。其中,Type类型可以表示任何类型的信息,包括基本类型、结构体、接口类型、函数类型等。Value类型可以表示任何值,包括基本类型、结构体、接口类型、函数类型等。Kind类型则表示值的种类,如int、float、string等。
    通过reflect包,可以获取一个类型的信息,例如:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type MyStruct struct {
    	Name string
    	Age  int
    }
    
    func main() {
    	t := reflect.TypeOf(MyStruct{})
    	fmt.Println("Type:", t.Name())
    	fmt.Println("Kind:", t.Kind())
    	for i := 0; i < t.NumField(); i++ {
    		field := t.Field(i)
    		fmt.Printf("%s (%s)\n", field.Name, field.Type.Name())
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    实现结果:
    在这里插入图片描述
    上述代码中,使用reflect.TypeOf函数获取MyStruct类型的信息,然后打印出类型名称、类型种类和字段信息。
    同时,也可以通过reflect包获取一个值的信息,例如:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type MyStruct struct {
    	Name string
    	Age  int
    }
    
    func main() {
    	v := MyStruct{Name: "Tom", Age: 18}
    	rv := reflect.ValueOf(v)
    	fmt.Println("Type:", rv.Type().Name())
    	fmt.Println("Kind:", rv.Kind())
    	for i := 0; i < rv.NumField(); i++ {
    		field := rv.Field(i)
    		fmt.Printf("%s: %v\n", field.Type().Name(), field.Interface())
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    实现结果:
    在这里插入图片描述
    上述代码中,使用reflect.ValueOf函数获取MyStruct类型的值信息,并通过rv.Type()函数获取值的类型信息,通过rv.Kind()函数获取值的种类信息,并打印出字段名称和值。同时,还可以通过rv.FieldByName函数获取指定名称的字段信息,并通过rv.FieldByNameFunc函数获取符合条件的字段信息。
    除了获取类型和值的信息之外,反射还可以动态地创建类型、创建值、调用方法和修改属性等。例如,可以使用reflect.New函数动态创建一个新的值,可以使用reflect.ValueOf函数设置一个值的属性,可以使用reflect.MethodByName函数调用一个方法,可以使用reflect.ValueOf函数修改一个值的属性等。

    2. 简单的反射实现修改变量的值

    下面示例中,简单演示了如何直接修改City变量的值:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type Address struct {
    	City  string
    	State string
    }
    type Person struct {
    	Name    string
    	Age     int
    	Address Address
    }
    
    func main() {
    	p := Person{
    		Name: "Alice",
    		Age:  25,
    		Address: Address{
    			City:  "New York",
    			State: "NY",
    		},
    	}
    	// 通过反射获取到嵌套结构体中需要修改的字段的反射值
    	field := reflect.ValueOf(&p).Elem().FieldByName("Address").FieldByName("City")
    	// 判断该字段是否可修改,如果不可修改,则需要使用 `Elem()` 函数获取到该字段的可修改的反射值
    	if !field.CanSet() {
    		field = field.Elem()
    	}
    	// 根据需要修改的值的类型,使用反射包中的 `SetValue()` 函数来修改该字段的值
    	field.SetString("Los Angeles")
    	// 输出: {Alice 30 {Los Angeles NY}}
    	fmt.Println("the result is:", p)
    }
    
    • 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

    输出结果:
    在这里插入图片描述
    然而,在实际运用中,第一种方式往往不够灵活,对于使用者而言,更多的想直接调用某个方法,通过传入需要修改的变量的路径以及修改后的值来直接实现,基于此,对第一种方式进行改进。

    3. 实现传入路径修改变量的值

    通过将具体操作封装为ModifyValue函数来实现,传入路径后,对路径进行拆解

    package main
    
    import (
    	"fmt"
    	"reflect"
    	"strings"
    )
    
    type Address struct {
    	City  string
    	State string
    }
    type Person struct {
    	Name    string
    	Age     int
    	Address Address
    }
    
    func ModifyValue(req *Person, modifyPath string, value interface{}) bool {
    	// 通过反射获取 req 的值
    	reqValue := reflect.ValueOf(req).Elem()
    	// 根据 path 找到 req 中需要修改的字段
    	substrings := strings.Split(modifyPath, ".")
    	for i, substring := range substrings {
    		if i == 0 {
    			continue
    		}
    		reqValue = reqValue.FieldByName(substring)
    		if !reqValue.IsValid() {
    			return false
    		}
    	}
    	// 将 value 转换成需要修改的字段的类型
    	newValue := reflect.ValueOf(value).Convert(reqValue.Type())
    	// 设置修改后的值
    	reqValue.Set(newValue)
    	return true
    }
    
    func main() {
    	req := Person{
    		Name: "Tom",
    		Age:  12,
    		Address: Address{
    			City:  "New York",
    			State: "NY",
    		},
    	}
    	result := ModifyValue(&req, "req.Address.City", "Los Angeles")
    	fmt.Printf("the result is:%v, req.Address.City:%v", result, req.Address.City)
    }
    
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    实现结果:
    在这里插入图片描述
    总之,Go语言的反射机制为程序提供了一种动态地获取类型信息和操作对象的能力,但是反射的效率较低,也要谨慎使用。

  • 相关阅读:
    Linux入门学习指南
    web前端-javascript-基本语法(注释,常用语法,代码格式)
    Java——二叉搜索树
    八月份跳槽了,历经华为测开岗4轮面试,不出意外,被刷了...
    Qt 定制专属闹钟
    python字符串的进阶
    农村污水处理站如何实现数据采集和远程监控?
    汽车行业深度报告:域控制器,汽车电子电气架构演进下的黄金赛道
    BOM介绍以及应用以及this指向问题
    FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+图像缩放,提供3套工程源码和技术支持
  • 原文地址:https://blog.csdn.net/qq_38233258/article/details/132914876