Go语言反射使用技巧

简介: interface(接口) 类型是一种特殊的类型,interface 用来表示一组方法集合,所有实现该方法集合的类型都可以被认为是实现了该接口。所以空 interface 类型的方法集合为空,也就是说所有类型都可以认为是实现了该接口。

Go 语言使用其自带的 reflect 包来实现反射。其反射机制就是程序在运行过程中,可以动态地调用对象的方法和属性。

我们知道,一个变量由类型和值两部分组成,类型又包括静态类型具体类型。静态类型就是在编码时可见的类型(int、string等),而具体类型是在程序运行时系统所见的类型。


interface

interface(接口) 类型是一种特殊的类型,interface 用来表示一组方法集合,所有实现该方法集合的类型都可以被认为是实现了该接口。所以空 interface 类型的方法集合为空,也就是说所有类型都可以认为是实现了该接口

反射有两种类型 reflect.Valuereflect.Type ,分别表示变量的值和类型,并且提供了两个函数 reflect.ValueOfreflect.TypeOf分别获取任意对象的 reflect.Valuereflect.Type

使用示例:

packagemainimport (
"fmt""reflect")
funcmain(){
varxfloat64=3.4t :=reflect.TypeOf(x)
fmt.Println("type:", t)
v :=reflect.ValueOf(x)
fmt.Println("value", v)
}
//运行结果//type: float64//value 3.4

reflect

  1. reflect.Value

reflect.ValueOf() 定义如下:

funcValueOf(iinterface{}) Value {...}

ValueOf() 函数用来获取输入参数接口中的数据的值。如果接口为空,则返回 0。

Value 类型为:

typeValuestruct {
typ*rtypeptrunsafe.Pointerflag}

示例,修改 struct 结构体字段的值:

funcmain() {
p :=person{Name: "微客鸟窝",Age: 18}
pv:=reflect.ValueOf(&p)
pv.Elem().Field(0).SetString("无尘")
fmt.Println(p)
}
typepersonstruct {
NamestringAgeint}


  1. reflect.Type

reflect.TypeOf() 定义如下:

funcTypeOf(iinterface{}) Type {...}

TypeOf() 函数用来动态获取输入参数接口中的值的类型,如果接口为空,则返回 nil。

reflect.Type 实际上是一个接口,定义了很多方法来获取类型相关的信息:

typeTypeinterface {
Implements(uType) bool//方法用于判断是否实现了接口 u;AssignableTo(uType) bool//方法用于判断是否可以赋值给类型 u,其实就是是否可以使用 =,即赋值运算符;ConvertibleTo(uType) bool//方法用于判断是否可以转换成类型 u,其实就是是否可以进行类型转换;Comparable() bool//方法用于判断该类型是否是可比较的,其实就是是否可以使用关系运算符进行比较。//以下这些方法和Value结构体的功能相同Kind() KindMethod(int) MethodMethodByName(string) (Method, bool)
NumMethod() intElem() TypeField(iint) StructFieldFieldByIndex(index []int) StructFieldFieldByName(namestring) (StructField, bool)
FieldByNameFunc(matchfunc(string) bool) (StructField, bool)
NumField() int}

示例,遍历结构体的字段和方法:

packagemainimport (
"fmt""reflect")
funcmain() {
p :=person{Name: "微客鸟窝", Age: 18}
pt :=reflect.TypeOf(p)
//遍历person的字段fori :=0; i<pt.NumField(); i++ {
fmt.Println("字段:", pt.Field(i).Name)
    }
//遍历person的方法fori :=0; i<pt.NumMethod(); i++ {
fmt.Println("方法:", pt.Method(i).Name)
    }
}
typepersonstruct {
NamestringAgeint}
func (pperson) String() string{
returnfmt.Sprintf("Name is %s,Age is %d",p.Name,p.Age)
}

反射3大法则

  1. 反射可以将 interface 类型变量转换成反射对象

通过 reflect 包的一些函数,可以把接口转换为反射定义的对象。

  • reflect.ValueOf() 获取某个变量的值
  • reflect.TypeOf() 获取某个变量的静态类型
  • reflect.Value.Kind() 获取变量值的底层类型,底层类型可能为int/float/struct/slice等
  • reflect.Value.Type() 获取变量值的类型,等同于reflect.TypeOf()  
  1. 反射可以将反射对象还原成 interface 对象
packagemainimport (
"fmt""reflect")
funcmain(){
varxfloat64=3.4v :=reflect.ValueOf(x) //v is reflext.Valuevaryfloat64=v.Interface().(float64)
fmt.Println("value", y)
}
//运行结果://value 3.4

对象 x 转换成反射对象 v,v 又通过 Interface() 接口转换成了 interface 对象,interface 对象通过.(float64)类型断言获取 float64 类型的值。

断言格式为:s = x.(T),意思是如果 x 所持有的元素如果同样实现了 T 接口,那么就把值传递给 s。

  1. 反射对象可修改,value值必须是可设置的

当使用 TypeOf() 和 ValueOf() 时,如果传递的不是接口变量的指针,那么反射里的变量值是一个副本值,对反射对象进行修改时,并不能修改真实的值。

错误示例:

packagemainimport (
"reflect")
funcmain(){
varxfloat64=3.4v :=reflect.ValueOf(x) //v is reflext.Valuev.SetFloat(6.6) //Error}

上面的程序会发生 panic ,因为 v 是不可修改的。

  • 传入 `reflect.ValueOf()` 函数的其实是 x 的副本值,而并非 x 本身。所以通过 v 修改其值是无法影响 x 的,所以会报错。
  • 若要修改,我们可以在 ValueOf()中传入 x 的地址,此时 v 代表的是指针地址,如何通过指针地址 v 修改 x 的值呢?
  • 使用 reflect.Value 的 Elem() 方法,可以获得指针指向的 value 。

示例:

packagemainimport (
"fmt""reflect")
funcmain(){
varxfloat64=3.4v :=reflect.ValueOf(&x)
v.Elem().SetFloat(6.6)
fmt.Println("x :", v.Elem().Interface())
}
//运行结果//x : 6.6
相关文章
|
1天前
|
Java 编译器 Go
探索Go语言的性能优化技巧
在本文中,我们将深入探讨Go语言的底层机制,以及如何通过代码层面的优化来提升程序性能。我们将讨论内存管理、并发控制以及编译器优化等关键领域,为你提供一系列实用的技巧和最佳实践。
|
1天前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
1天前
|
安全 Go 调度
探索Go语言的并发模式:协程与通道的协同作用
Go语言以其并发能力闻名于世,而协程(goroutine)和通道(channel)是实现并发的两大利器。本文将深入了解Go语言中协程的轻量级特性,探讨如何利用通道进行协程间的安全通信,并通过实际案例演示如何将这两者结合起来,构建高效且可靠的并发系统。
|
1天前
|
安全 Go 开发者
破译Go语言中的并发模式:从入门到精通
在这篇技术性文章中,我们将跳过常规的摘要模式,直接带你进入Go语言的并发世界。你将不会看到枯燥的介绍,而是一段代码的旅程,从Go的并发基础构建块(goroutine和channel)开始,到高级模式的实践应用,我们共同探索如何高效地使用Go来处理并发任务。准备好,让Go带你飞。
|
2天前
|
运维 Go 开发者
Go语言在微服务架构中的应用与优势
本文深入探讨了Go语言在构建微服务架构中的独特优势和实际应用。通过分析Go语言的核心特性,如简洁的语法、高效的并发处理能力以及强大的标准库支持,我们揭示了为何Go成为开发高性能微服务的首选语言。文章还详细介绍了Go语言在微服务架构中的几个关键应用场景,包括服务间通信、容器化部署和自动化运维等,旨在为读者提供实用的技术指导和启发。
|
2天前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
3天前
|
Go 开发者
Go语言中的并发编程:从基础到实践
在当今的软件开发中,并发编程已经成为了一项不可或缺的技能。Go语言以其简洁的语法和强大的并发支持,成为了开发者们的首选。本文将带你深入了解Go语言中的并发编程,从基础概念到实际应用,帮助你掌握这一重要的编程技能。
|
4天前
|
Go
使用go语言将A助手加入项目中
使用go语言将A助手加入项目中
13 2
|
6天前
|
负载均衡 Go API
探索Go语言在微服务架构中的应用与优势
在这篇技术性文章中,我们将深入探讨Go语言(又称为Golang)在构建微服务架构时的独特优势。文章将通过对比分析Go语言与其他主流编程语言,展示Go在并发处理、性能优化、以及开发效率上的优势。同时,我们将通过一个实际的微服务案例,详细说明如何利用Go语言构建高效、可扩展的微服务系统。
|
4天前
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。