goweb编程实战----反射(代码片段)

李元静 李元静     2022-10-23     452

关键词:

目录

反射

与其他语言一样,Go语言的反射同样是指,计算机程序在运行时,可以访问、检测和修改它本身状态或行为的一种能力。

其在reflect包里,定义了一个接口和一个结构体,即reflect.Type接口与reflect.Value结构体,它们提供了很多函数来获取存储在接口里的类型信息。

  • reflect.Type接口:主要提供关于类型相关的信息
  • reflect.Value结构体:主要提供关于值相关的信息,可以获取甚至改变类型的值。

reflect包中提供了两个基础的关于反射的函数,用来获取上述接口与结构体:

func TypeOf(i interface()) Type
func ValueOf(I interface()) Value

其中:

  • TypeOf()函数:用来提取一个接口中值的类型信息。由于它的输入参数是一个空的interface,所以在调用此函数时,实参会先被转换为interface类型。这样,实参的类型信息、方法集、值信息都存储到interface变量里了。
  • ValueOf()函数:返回一个结构体变量,包含类型信息及实际值。

详细原理图如下:

反射的3大原则

在Go语言中,反射有3大原则:

  • 反射可以将”接口类型变量“转换为”反射类型对象“
  • 反射可以将”反射类型对象“转换为”接口类型变量“
  • 如果要修改”反射类型对象“,则其值必须是“可写的”

“接口类型变量”转换为“反射类型对象”

反射是一种检查存储在接口变量中的类型与值对的机制。reflect包中的两个类型:Type和Value。这2种类型给了我们访问一个接口变量种所包含的内容的途径。

另外,2个简单的函数reflect.TypeOf()和reflect.ValueOf()可以检索一个接口值的reflect.Type与reflect.Value部分。示例如下:

import (
	"fmt"
	"reflect"
)

func main() 
	var x float64 = -3.151592653
	fmt.Println("Type:", reflect.TypeOf(x))
	v := reflect.ValueOf(x)
	fmt.Println("Value:", v)
	fmt.Println("Type:", v.Type())
	fmt.Println("is float64:", v.Kind() == reflect.Float64)
	fmt.Println("Value:", v.Float())

运行之后,我们会得到如下结果。

上面代码,我们先是定义了一个float64的变量,然后将其赋值给reflect.TypeOf(x)函数。在我们调用该函数时,x会被保存到空接口中,然后这个空接口作为参数传递,reflect.TypeOf会将空接口拆包恢复出类型信息。

当然,reflect.ValueOf(x)同样也可以将值恢复出来,而且reflect.ValueOf获取的变量类型还可以使用方法进行操作。比如获取类型,值,以及对比类型。

“反射类型对象”转换为“接口类型变量”

这个与上面的正好相反,和物理学类似,有物质就有反物质。

在Go语言中,反射也能创造自己反面类型的对象。根据reflect.Value类型的变量,可以使用interface()方法恢复其接口类型的值。而且该方法,会把type和value信息打包并填充到一个接口变量中,然后返回。定义如下:

//定义
func (v Value) Interface() interface
//例子
func main() 
	var name interface="liyuanjing"
	x :=reflect.TypeOf(name)
	y :=reflect.ValueOf(name)
	//从接口变量到反射对象
	fmt.Printf("从接口变量到反射对象:Type对象的类型为%T \\n",x)
	fmt.Printf("从接口变量到反射对象:Value对象的类型为%T \\n",y)
	//从反射对象到接口变量
	z :=y.Interface()
	fmt.Printf("从反射对象到接口变量:新对象的类型为%T 值为%v \\n",z,z)

运行之后,控制台输出效果如下:

“反射类型对象”修改(值必“可写的”)

在使用reflect.TypeOf()函数和reflect.ValueOf()函数时,如果传递的不是接口变量的指针,则反射世界里的变量始终只是真实世界里的一个复制,对该反射对象进行修改,并不能反映到真实世界里。

需要注意的是:

  • 不是接收变量指针创建的反射对象,是不具备“可写性”的
  • 是否具备“可写性”,可使用CanSet()方法来判断
  • 对不具备“可写性”的对象进行修改,是没有意义的,也认为是不合法的,因此会报错。

如果需要让反射具备可写性,需要这样:

  • 创建反射对象时,传入变量的是指针
  • 使用Elem()方法,返回指针指向的数据。

判断可写性示例:

func main() 
	var name string = "liyuanjing"
	x := reflect.ValueOf(&name)
	fmt.Println("x的可写性为:", x.CanSet())
	y := x.Elem()
	fmt.Println("y的可写性为:", y.CanSet())

运行之后,你会发现x是不可写的,y因为使用了Elem()方法,是可写的。


知道了如何使用反射世界里的对象具有可写性后,接下来是时候了解一下,如何对修改更新对象了。

在反射的Value对象中,有多个以单词Set开头的方法用于重新设置对应类型的值。比如:

func (v Value) SetBool(x bool) 
func (v Value) SetBytes(x []byte) 
func (v Value) setRunes(x []rune)
func (v Value) SetComplex(x complex128)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64) 

这些方法全部都是修改值的入口,比如,通过反射对象SetInt()方法进行更新值的示例如下:

func main() 
	var num int = 30
	fmt.Println("原始值为:", num)
	x := reflect.ValueOf(&num)
	y := x.Elem()

	y.SetInt(500)
	fmt.Println("通过反射对象进行更新后,num的真实值为:", num)

运行之后,效果如下:

goweb编程实战----函数(代码片段)

目录前言声明与使用函数返回多个值return可以为空函数参数参数值传递引用传递可选函数匿名函数匿名函数的定义匿名函数的调用回调函数defer延迟语句defer与return的执行顺序defer常用场景前言本篇博文主要介绍Go语言的函数定义... 查看详情

goweb编程实战----函数(代码片段)

目录前言声明与使用函数返回多个值return可以为空函数参数参数值传递引用传递可选函数匿名函数匿名函数的定义匿名函数的调用回调函数defer延迟语句defer与return的执行顺序defer常用场景前言本篇博文主要介绍Go语言的函数定义... 查看详情

goweb编程实战----数据类型(代码片段)

...类型、切片类型、Map类型以及结构体类型。布尔型与其他编程语言一样,Go语言的布尔型只可以是tr 查看详情

goweb编程实战----面向对象编程(代码片段)

...有类的概念,但这并不意味着Go语言不支持面向对象编程,毕竟面向对象只是一种编程思想。封装属性其实,学习过C语言都应该清楚,结构体是一个类类的结构,也就是说结构体是类的一种简化形式。 查看详情

goweb编程实战----面向对象编程(代码片段)

...有类的概念,但这并不意味着Go语言不支持面向对象编程,毕竟面向对象只是一种编程思想。封装属性其实,学习过C语言都应该清楚,结构体是一个类类的结构,也就是说结构体是类的一种简化形式。 查看详情

goweb编程实战----流程控制语句(代码片段)

目录流程控制语句if-else语句for循环语句用for循环实现do-while用for循环实现whilebreak指定跳出循环continue语句for-range循环遍历数组遍历字符串遍历map遍历通道(channel)switch-case语句一个分支多个值分支表达式goto语句流程控制... 查看详情

goweb编程实战----流程控制语句(代码片段)

目录流程控制语句if-else语句for循环语句用for循环实现do-while用for循环实现whilebreak指定跳出循环continue语句for-range循环遍历数组遍历字符串遍历map遍历通道(channel)switch-case语句一个分支多个值分支表达式goto语句流程控制... 查看详情

goweb编程实战----并发goroutine(代码片段)

目录什么是goroutine?使用方式什么是goroutine?在Go语言中,每一个并发执行的活动被称为goroutine。使用go关键字可以创建goroutine,其完整定义如下:gofunc_name()其中,go是关键字,需要放在一个需要调用的函数之前&#... 查看详情

goweb编程实战----并发goroutine(代码片段)

目录什么是goroutine?使用方式什么是goroutine?在Go语言中,每一个并发执行的活动被称为goroutine。使用go关键字可以创建goroutine,其完整定义如下:gofunc_name()其中,go是关键字,需要放在一个需要调用的函数之前&#... 查看详情

goweb编程实战----并发goroutine(代码片段)

目录什么是goroutine?使用方式什么是goroutine?在Go语言中,每一个并发执行的活动被称为goroutine。使用go关键字可以创建goroutine,其完整定义如下:gofunc_name()其中,go是关键字,需要放在一个需要调用的函数之前&#... 查看详情

goweb编程实战----创建客户端(代码片段)

目录创建一个客户端Get()请求Post()请求请求头设置创建一个客户端在Go语言的net/http包中,还提供了一个被称为Client的结构体。该结构体提供了Get()、Post()两个请求函数。下面,我们来分别使用代码实现这些请求。Get()请求... 查看详情

goweb编程实战----go语言的基础语法(代码片段)

目录前言HelloWorld包的声明与导入运行程序方式基础语法注释标识符变量常量运算符前言很高兴能够一起和大家学习Go语言,对于它的场景其实很多,包括云计算、容器虚拟化、分布式存储、网络爬虫、运维开发、Web开发... 查看详情

goweb编程实战----go语言的基础语法(代码片段)

目录前言HelloWorld包的声明与导入运行程序方式基础语法注释标识符变量常量运算符前言很高兴能够一起和大家学习Go语言,对于它的场景其实很多,包括云计算、容器虚拟化、分布式存储、网络爬虫、运维开发、Web开发... 查看详情

goweb编程实战----创建http与https服务器端(代码片段)

目录创建一个HTTP服务端ListenAndServe()ServeHTTP()方法的使用示例定义Refer结构体实现ServeHTTP()方法创建一个HTTPS服务端创建证书与私钥创建HTTPS服务端创建一个HTTP服务端其实Go语言创建一个简单的HTTP服务器程序,与JavaWeb的servlet差... 查看详情

goweb编程实战(10)----模板引擎库text/template包的使用(代码片段)

目录前言模板引擎定义模板文件解析模板文件渲染模板实战使用模板创建.tmpl文件创建文件用于解析与渲染模板前言在Go语言中,模板引擎库text/template包主要用于处理任意格式的文本内容,同时还提供了html/template包,... 查看详情

goweb编程.基础入门(代码片段)

GoWeb一,反射二,HelloWorld1,服务器端创建2,HTTPS服务器端3,创建简单客户端Ⅰ,基础知识Ⅱ,创建GET请求Ⅲ,创建POST请求4,html/template包三,接收处理GoWeb请求1,接收请求Ⅰ,Serve 查看详情

goweb编程实战----创建客户端(代码片段)

目录创建一个客户端Get()请求Post()请求请求头设置创建一个客户端在Go语言的net/http包中,还提供了一个被称为Client的结构体。该结构体提供了Get()、Post()两个请求函数。下面,我们来分别使用代码实现这些请求。Get()请求... 查看详情

goweb编程实战----创建客户端(代码片段)

目录创建一个客户端Get()请求Post()请求请求头设置创建一个客户端在Go语言的net/http包中,还提供了一个被称为Client的结构体。该结构体提供了Get()、Post()两个请求函数。下面,我们来分别使用代码实现这些请求。Get()请求... 查看详情