Go Web编程实战(6)----反射

简介: Go Web编程实战(6)----反射

反射


与其他语言一样,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)
}

运行之后,效果如下:


相关文章
|
1月前
|
存储 人工智能 Go
Go-Zero全流程实战即时通讯
Go-Zero 是一个功能丰富的微服务框架,适用于开发高性能的即时通讯应用。它具备中间件、工具库和代码生成器,简化开发流程。本文介绍其环境搭建、项目初始化及即时通讯功能实现,涵盖用户认证、消息收发和实时推送,帮助开发者快速上手。
164 0
|
1月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
1月前
|
Go 开发者
Go语言实战案例:使用select监听多个channel
本文为《Go语言100个实战案例 · 网络与并发篇》第5篇,详解Go并发核心工具`select`的使用。通过实际案例讲解如何监听多个Channel、实现多任务处理、超时控制和非阻塞通信,帮助开发者掌握Go并发编程中的多路异步事件处理技巧。
|
1月前
|
数据采集 编解码 监控
Go语言实战案例:使用channel实现生产者消费者模型
本文是「Go语言100个实战案例 · 网络与并发篇」第4篇,通过实战案例详解使用 Channel 实现生产者-消费者模型,涵盖并发控制、任务调度及Go语言并发哲学,助你掌握优雅的并发编程技巧。
|
1月前
|
数据采集 消息中间件 编解码
Go语言实战案例:使用 Goroutine 并发打印
本文通过简单案例讲解 Go 语言核心并发模型 Goroutine,涵盖协程启动、输出控制、主程序退出机制,并结合 sync.WaitGroup 实现并发任务同步,帮助理解 Go 并发设计思想与实际应用。
|
1月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
2月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
2月前
|
存储 算法 数据可视化
Go语言实战:图的邻接表表示法实现详解
本文是《Go语言100个实战案例》系列之一,讲解图的邻接表表示法及其在Go语言中的实现。适用于稀疏图,节省空间,适合初学者与进阶开发者学习图结构在工程中的应用。
|
2月前
|
机器学习/深度学习 存储 算法
Go语言实战案例-广度优先遍历BFS
广度优先遍历(BFS)是一种层级展开的搜索策略,常用于树与图的遍历、最短路径查找、二维数组中的感染扩散等问题。它借助队列实现,优先访问当前层所有节点,再进入下一层,适用于寻找最短路径、层序遍历、岛屿问题等场景。
|
2月前
|
算法 Go C++
Go语言实战案例-深度优先遍历DFS
深度优先遍历(DFS)是一种用于遍历图和树结构的重要算法,其核心思想是“一条路走到底”,即沿着每个分支尽可能深入,直到无法继续再回溯。在树中,DFS包括前序、中序和后序三种遍历方式;在图中,DFS可用于寻找路径、计算连通分量、拓扑排序等。该算法通常通过递归或栈实现,适用于解决岛屿数量、迷宫路径、括号生成等经典问题。本文还对比了DFS与BFS的区别,并介绍了其在不同场景下的应用与实现方法。