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
相关文章
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
10天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
|
4天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
13 2
|
4天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
15 2
|
9天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
31 7
|
9天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
38 5
|
8天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
25 1
|
8天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
84 58
|
7天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
8天前
|
存储 编译器 Go
go语言中的变量、常量、数据类型
【11月更文挑战第3天】
25 9