golang-反射(reflect)

本文详细介绍了Go语言中的反射机制,包括TypeOf()和ValueOf()函数的使用,反射类型转换的重要图示,以及反射操作中的注意事项和细节。通过实例展示了如何对struct类型进行反射操作,修改指针类型变量的值,以及如何通过反射获取struct字段的tag。同时,文中还提到了常量的定义和特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

TypeOf()和ValueOf()函数

  • TypeOf() 这个函数可以接收任何类型的参数,会返回一个该参数的相关参数(接口类型)
    在这里插入图片描述
    在这里插入图片描述

  • ValueOf 这个函数可以传入任意类型的参数,能够返回一个结构体类型
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 这里绑定ValueOf函数传入参数返回的value结构体

  • 可以使用该结构体绑定的相应方法如下所示:
    在这里插入图片描述

反射入门:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下边两图为反射类型转换的重要图示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

// 演示reflect的快速入门
package main

import (
	"fmt"
	"reflect"
)

// 演示反射 函数参数为空接口则可传入任意类型
func reflectlist(a interface{}){
	// 通过反射获取传入参数的 type/kind/值
	// 1.先获取到reflect.Type 
	// func TypeOf(i interface{}) 
	rTyp := reflect.TypeOf(a)
	fmt.Println(rTyp)

	// 2.获取到reflect.Value [func ValueOf(i interface{}) Value]
	rValue := reflect.ValueOf(a)
	// rValue真实type并不是int类型而是reflect.Value
	fmt.Printf("rValue type = %T value = %v\n",rValue,rValue)

	// rValue.Int()将其类型转为int64,就可以实现int类型操作
	rInt := rValue.Int()
	fmt.Printf("rInt type = %T value = %v\n",rInt,rInt+10)

	// 将rValue转为interface{}
	iv := rValue.Interface()
	num2 := iv.(int)
	fmt.Printf("num2 type = %T value = %v\n",num2,num2)
}

func main(){
	a := 10
	reflectlist(a)
}
int
rValue type = reflect.Value value = 10
rInt type = int64 value = 20
num2 type = int value = 10

在这里插入图片描述
上边这个不太好解释,反射根本上就是运行时反射,通过reflect.ValueOf()可以返回该变量由接口保管的值,然后可以将该值转为interface{}空接口类型,但是运行时仍然是传入的变量类型,所以如果要对该变量值进行操作就需要通过类型断言就行转化才能进行操作

演示对struct类型的反射操作:

package main

import (
	"fmt"
	"reflect"
)

type Person struct{
	name string
	age int
}

type Json struct{
	name string
	age int
}

func shwomaker(show interface{}){
	rTyp := reflect.TypeOf(show)

	// rValue为反射出来的
	rValue := reflect.ValueOf(show)
	fmt.Printf("TypeOf=%v ValueOf=%v\n",rTyp,rValue)

	// 此时rvia虽然被转为interface{}类型但是打印出type仍是Person类型
	// 虽然是Person类型但编译时候并不能识别出来无法取出结构体里的值
	// 需要通过类型断言转为Person类型才能正常取出
	rvia := rValue.Interface()
	fmt.Printf("rviatype=%T value=%v\n",rvia,rvia)

	// 这里使用一个带判断的类型断言,如果rvia断言成功则ok为true否则为false
	// 这里使用类型断言转化成功则可以取出stu里的name字段(Person类型)
	stu,ok := rvia.(Person)
	if ok{
		fmt.Printf("stu.type=%T stu.value=%v stu.name=%v",stu,stu,stu.name)
	}
}

func main(){
	var a = Person{"tom",18}
	shwomaker(a)
}
TypeOf=main.Person ValueOf={tom 18}
rviatype=main.Person value={tom 18}
stu.type=main.Person stu.value={tom 18} stu.name=tom

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 类别栗子来说:类型=变量/类别=常量
  • 就像是我们在淘宝买衣服,男装里类型有大衣,卫衣,毛衣,类别为男装
package main

import (
	"fmt"
	"reflect"
)
type Person struct{
	name string
	age int
}

func main(){
	// a的类型为Person类别为struct
	var a Person
	fmt.Printf("a.type=%T\n",a)

	// Kind()返回该对象的类别
	rTyp := reflect.ValueOf(a)
	rkind := rTyp.Kind()
	fmt.Printf("a.kind=%v",rkind)
}

shell输出可知a的type为main.Person,类别为struct

a.type=main.Person
a.kind=struct

细节五:
通过反射来修改指针类型的变量需要 Elem() 来返回指针指向的值,然后使用 SetInt() 来进行修改
int类型就使用SetInt,string就是用SetString其他类型也是一样
在这里插入图片描述
在这里插入图片描述

package main

import (
	"fmt"
	"reflect"
)

func reflectdemo(a interface{}){
	rValue := reflect.ValueOf(a)
	fmt.Printf("rValue.value=%v rValue.type=%T\n",rValue,rValue)

	// Elem()返回rValue持有的指针指向的值,然后再使用SetInt()进行更改
	rValue.Elem().SetInt(20)
}

func main(){
	var a int
	a = 10
	reflectdemo(&a)

	fmt.Println(a)
}
rValue.value=0xc042046058 rValue.type=reflect.Value
20

细节六:
在这里插入图片描述

反射课堂练习:

在这里插入图片描述

题一:

package main

import (
	"fmt"
	"reflect"
)
func reflectdemo(re interface{}){
	rValue := reflect.ValueOf(re)
	rTyp := reflect.TypeOf(re)
	fmt.Printf("rValue=%v rTyp=%v \n",rValue,rTyp)

	rValueKind := rValue.Kind()
	fmt.Println(rValueKind)

	rInterface := rValue.Interface()
	fmt.Printf("rInterface.Value=%v rInterface.type=%T\n",rInterface,rInterface)

	rFloat64 := rInterface.(float64)
	rFloat64 = 7.64
	fmt.Println(rFloat64)
}

func main(){
	var re float64
	re = 0.618
	reflectdemo(re)

	fmt.Println("re=",re)
}

题二:
描述错误,ValueOf()下的SetInt()/SetString()方法是用来修改pointer类型的变量语法为:

变量.Elem().SetInt(新值)
package main

import (
	"fmt"
	"reflect"
)
func main(){
	a := "tom"
	rValue := reflect.ValueOf(&a)
	rValue.Elem().SetString("jack")
	fmt.Println(a)
}

在这里插入图片描述

struct字段添加tag反射原理解析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • struct实例传入函数后初始化为ValueOf()和TypeOf()
  • 通过Kind() 方法获取到变量初始为ValueOf的类型加一个判断如果不是struct类型就退出
  • 再通过V下的方法NumField()获取到变量字段数
  • rTyp.Field(i).Tag.Get("json") T下的这一坨方法最后的Get()方法传入一个struct tag的key值,如果匹配到就返回这个tag的value没有匹配到就返回为空
package main

import (
	"fmt"
	"reflect"
)
type Cat struct{
	Name string `json:"name"`
	Age int `json:"age"`
}

func reflectjson(p1 interface{}){
	rTyp := reflect.TypeOf(p1)
	rVal := reflect.ValueOf(p1)
	// 获取rVal的类型
	rValKind := rVal.Kind()
	fmt.Println(rValKind)
	// rValKind输出为struct但是要写为reflect.Struct
	if rValKind != reflect.Struct{
		return
	}

	// 返回rVal的字段数
	num := rVal.NumField()
	fmt.Println(num)

	for i:=0;i<num;i++{
		//Field 返回结构体的第i个字段(的Value封装)
		// 如果v的Kind不是Struct或i出界会panic
		// fmt.Printf("Field %d 值为%v\n",i,rVal.Field(i))

		// Field(i int) StructField
    	// 返回索引序列指定的嵌套字段的类型,
		// 等价于用索引中每个值链式调用本方法,如非结构体将会panic
		// Get方法返回标签字符串中键key对应的值。如果标签中没有该键
		// 会返回""。如果标签不符合标准格式,Get的返回值是不确定的。
		tagval := rTyp.Field(i).Tag.Get("json")
		if tagval != ""{
			fmt.Printf("字段:%v tag为:%v\n",rVal.Field(i),tagval)
		}
	}
}

func main(){
	c1 := Cat{"tom",20}
	reflectjson(c1)
}
struct
2
字段:tom tag为:name
字段:20 tag为:age

反射终极案例:

  • 使用反射获取传入的struct类型字段tag,传值调用绑定此struct的方法

注意事项:

  • struct绑定的方法名首字母需大写否则反射跨包检测传入变量绑定方法检测不到
  • 反射调用方法主要使用value下的方法
  • 方法排序是按照方法名的ASCII码排序,并不是按顺序排序,调用需注意
  • rValue.Method(1).Call(nil) | func (v Value) Call(in []Value) []Value Call方法传入参数需要是一个[]slice类型,同样返回一个[]slice,方法如果有一个返回值则此返回的[]slice有一个元素,同原理
package main

import (
	"fmt"
	"reflect"
)

type Person struct{
	Name string `json:"name"`
	Age int `json:"age"`
}

// 方法名首字母需大写否则反射跨包检测方法检测不到
func (p Person)Cat(n1 int,n2 int)int{
	return n1 + n2
}

func (p Person)Date(){
	fmt.Println(p.Name,p.Age)
}

func Tian(showmaker interface{}){
	rTyp := reflect.TypeOf(showmaker)
	rValue := reflect.ValueOf(showmaker)
	// 获取rvalue.kind
	rValueKind := rValue.Kind()
	// if rvalue!=reflect.value return
	if rValueKind != reflect.Struct{
		return
	}
	// 获取rValue所持有的字段个数
	num := rValue.NumField()

	for i:=0;i<num;i++{
		// tag标签匹配到json则把字段tag返回给tag
		tag := rTyp.Field(i).Tag.Get("json")
		fmt.Printf("value=%v tag=%v",rValue.Field(i),tag)
	}
	// 获取rvalue所持有的方法个数,方法首字母大写方能检测到
	nummuthod := rValue.NumMethod()
	fmt.Println(nummuthod)
	// Method参数为第几个方法,方法排序默认按照函数名排序(ASCII码)
	rValue.Method(1).Call(nil) //call为访问方法的参数,nil为不添加参数
	// 声明一个reflect.Value类型的slice
	var reslice []reflect.Value // 访问方法参数需要为value类型切片
	// 给reslice追加两个元素
	reslice = append(reslice,reflect.ValueOf(20))
	reslice = append(reslice,reflect.ValueOf(30))
	// rValue.Method访问第一个方法,并传入参数reslice
	res := rValue.Method(0).Call(reslice) //传入value类型切片返回value类型切片
	// 由于返回值为value类型切片,方法返回一个值,则取值为res[0]取第一个
	fmt.Println("res=",res[0].Int()) 
}

func main(){
	showmaker := Person{"tom",20}
	Tian(showmaker)
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


常量介绍

  • 常量由const修饰
  • 常量在定义必须初始化并赋值
  • 常量不能修改
  • 常量只能修饰bool,数值类型(int,float系列),string类型
  • 常量和普通变量一样有访问范围限制,写在main函数外为全局变量,main函数中为局部变量,首字母大写则可以被别的包引用,小写则只能本包使用
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    演示:
    表示给a赋值为0,b在a基础上+1,c在b基础+1
package main

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

常规用法:

package main

import (
	"fmt"
)
func main(){
	const(
		a = 10
		b = 0.16
		c = "string"
	)
	fmt.Println(a,b,c)
}
10 0.16 string
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值