Go语言反射(reflection)简述
来源:云海天教程
时间:2023-01-07 11:52:02 440浏览 收藏
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《Go语言反射(reflection)简述》,聊聊反射,我们一起来看看吧!
Go语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,但是在编译时并不知道这些变量的具体类型。这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。反射(reflection)是在 Java 出现后迅速流行起来的一种概念。通过反射,你可以获取丰富的类型信息,并可以利用这些类型信息做非常灵活的工作。
在 Java 中,你可以读取配置并根据类型名称创建对应的类型,这是一种常见的编程手法。Java 中的很多重要框架和技术(比如 Spring/IoC、Hibernate、Struts)等都严重依赖于反射功能。虽然,使用 Java EE 时很多人都觉得很麻烦,比如需要配置大量 XML 格式的配置程序,但这毕竟不是反射的错,反而更加说明了反射所带来的高可配置性。
大多数现代的高级语言都以各种形式支持反射功能,除了一切以性能为上的 C++ 语言。Go语言的反射实现了反射的大部分功能,但没有像 Java 语言那样内置类型工厂,故而无法做到像 Java 那样通过类型字符串创建对象实例。
反射是把双刃剑,功能强大但代码可读性并不理想。若非必要,并不推荐使用反射,下面我们将介绍反射功能在 Go语言中的具体体现以及反射的基本使用方法。
基本概念
Go语言中的反射与其他语言有比较大的不同。首先我们要理解两个基本概念 Type 和 Value,它们也是 Go语言包中 reflect 空间里最重要的两个类型。我们先看一下下面的定义:type MyReader struct { Name string}func (r MyReader)Read(p []byte) (n int, err error) { // 实现自己的Read方法}因为 MyReader 类型实现了 io.Reader 接口的所有方法(其实就是一个 Read() 函数),所以 MyReader 实现了接口 io.Reader。我们可以按如下方式来进行 MyReader 的实例化和赋值:
var reader io.Readerreader = &MyReader{"a.txt"}现在我们可以再来解释一下什么是 Type,什么是 Value。
对所有接口进行反射,都可以得到一个包含 Type 和 Value 的信息结构。比如我们对上例的 reader 进行反射,也将得到一个 Type 和 Value,Type 为 io.Reader,Value 为 MyReader{"a.txt"}。顾名思义,Type 主要表达的是被反射的这个变量本身的类型信息,而 Value 则为该变量实例本身的信息。
基本用法
通过使用 Type 和 Value,我们可以对一个类型进行各项灵活的操作。接下来我们分别演示反射的几种最基本用途。1) 获取类型信息
为了理解反射的功能,我们先来看看下面代码所示的这个小程序。package mainimport ( "fmt" "reflect")func main() { var x float64 = 3.4 fmt.Println("type:", reflect.TypeOf(x))}运行上述代码,输出结果如下所示:
type: float64
Type 和 Value 都包含了大量的方法,其中第一个有用的方法应该是 Kind,这个方法返回该类型的具体信息:Uint、Float64 等。Value 类型还包含了一系列类型方法,比如 Int(),用于返回对应的值。查看以下示例:var x float64 = 3.4v := reflect.ValueOf(x)fmt.Println("type:", v.Type())fmt.Println("kind is float64:", v.Kind() == reflect.Float64)fmt.Println("value:", v.Float())结果为:
type: float64
kind is float64: true
value: 3.4
2) 获取值类型
类型 Type 中有一个成员函数 CanSet(),其返回值为 bool 类型。如果你在注意到这个函数之前就直接设置了值,很有可能会收到一些看起来像异常的错误处理消息。可能很多人会置疑为什么要有这么个奇怪的函数,可以设置所有的域不是很好吗?这里先解释一下这个函数存在的原因。
我们在之前的学习中提到过 Go语言中所有的类型都是值类型,即这些变量在传递给函数的时候将发生一次复制。基于这个原则,我们再次看一下下面的语句:
var x float64 = 3.4v := reflect.ValueOf(x)v.Set(4.1)最后一条语句试图修改 v 的内容。是否可以成功地将 x 的值改为 4.1 呢?先要理清 v 和 x 的关系。在调用 ValueOf() 的地方,需要注意到 x 将会产生一个副本,因此 ValueOf() 内部对 x 的操作其实都是对着 x 的一个副本。
假如 v 允许调用 Set(),那么我们也可以想象出,被修改的将是这个 x 的副本,而不是 x 本身。如果允许这样的行为,那么执行结果将会非常困惑。调用明明成功了,为什么 x 的值还是原来的呢?为了解决这个问题 Go语言,引入了可设属性这个概念(Settability)。如果 CanSet() 返回 false,表示你不应该调用 Set() 和 SetXxx() 方法,否则会收到这样的错误:
panic: reflect.Value.SetFloat using unaddressable value
现在我们知道,有些场景下不能使用反射修改值,那么到底什么情况下可以修改的呢?其实这还是跟传值的道理类似。我们知道,直接传递一个 float 到函数时,函数不能对外部的这个 float 变量有任何影响,要想有影响的话,可以传入该 float 变量的指针。下面的示例小幅修改了之前的例子,成功地用反射的方式修改了变量 x 的值:var x float64 = 3.4p := reflect.ValueOf(&x) // 注意:得到X的地址fmt.Println("type of p:", p.Type())fmt.Println("settability of p:" , p.CanSet())v := p.Elem()fmt.Println("settability of v:" , v.CanSet())v.SetFloat(7.1)fmt.Println(v.Interface())fmt.Println(x)
对结构的反射操作
之前讨论的都是简单类型的反射操作,现在我们讨论一下结构的反射操作。下面的示例演示了如何获取一个结构中所有成员的值:type T struct { A int B string}t := T{203, "mh203"}s := reflect.ValueOf(&t).Elem()typeOfT := s.Type()for i := 0; i 以上例子的输出为:
0: A int = 203
1: B string = mh203
今天带大家了解了反射的相关知识,希望对你有所帮助;关于Golang的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
-
174 收藏
-
246 收藏
-
353 收藏
-
126 收藏
-
395 收藏
-
193 收藏
-
271 收藏
-
414 收藏
-
179 收藏
-
194 收藏
-
441 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习