Go语言通过反射获取各种类型变量的值

这篇具有很好参考价值的文章主要介绍了Go语言通过反射获取各种类型变量的值。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Go语言通过反射获取各种类型变量的值

反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。

1、什么是反射

反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。

Golang 反射包中有两对非常重要的函数和类型,两个函数分别是:

  • reflect.TypeOf 能获取类型信息 reflect.Type;

  • reflect.ValueOf 能获取数据的运行时表示 reflect.Value;

2、reflect.Type

Golang 是一门静态类型的语言,反射是建立在类型之上的。

通过 reflect.TypeOf() 函数可以获得任意值的类型信息。

2.1 类型Type和种类Kind

诸如 int32, slice, map 以及通过 type 关键词自定义的类型。

种类 Kind 可以理解为类型的具体分类,如 int32、type MyInt32 int32 是两种不同类型,但都属于 int32 这个种

类。

使用 reflect.TypeOf() 获取变量类型以及种类。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	type MyInt32 int32
	a := MyInt32(1)
	b := int32(1)
	// reflect.TypeOf(a):main.MyInt32 Kind:int32
	fmt.Printf("reflect.TypeOf(a):%v Kind:%v\n", reflect.TypeOf(a), reflect.TypeOf(a).Kind())
	// reflect.TypeOf(b):int32 Kind:int32
	fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", reflect.TypeOf(b), reflect.TypeOf(b).Kind())
}

从代码输出可以看出 int32、type MyInt32 int32 是两种不同类型,但都属于 int32 这个种类。

种类定义点击查看:

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

2.2 引用指向元素的类型

// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
Elem() Type

部分情况我们需要获取指针指向元素的类型、或者 slice 元素的类型,可以 reflect.Elem() 函数获取。

package main

import (
	"fmt"
	"reflect"
)

type myStruct struct {
}

func main() {
	a := &myStruct{}
	typeA := reflect.TypeOf(a)
	// TypeOf(a):*main.myStruct Kind:ptr
	fmt.Printf("TypeOf(a):%v Kind:%v\n", typeA, typeA.Kind())
	// TypeOf(a).Elem():main.myStruct Elem().Kind:struct
	fmt.Printf("TypeOf(a).Elem():%v Elem().Kind:%v\n", typeA.Elem(), typeA.Elem().Kind())
	s := []int64{}
	typeS := reflect.TypeOf(s)
	// TypeOf(s):[]int64 Kind:slice
	fmt.Printf("TypeOf(s):%v Kind:%v\n", typeS, typeS.Kind())
	// TypeOf(s).Elem():int64 Elem().Kind:int64
	fmt.Printf("TypeOf(s).Elem():%v Elem().Kind:%v\n", typeS.Elem(), typeS.Elem().Kind())
}

从代码输出可以看出,通过 reflect.Elem() 函数可以获取引用指向数据的类型。

2.3 结构体成员类型

通过 NumField 获取成员数量,Field 通过下标访问成员的类型信息 StructField,包括成员名称、类型、Tag 信息

等。

package main

import (
	"fmt"
	"reflect"
)

type secStruct struct {
	Cnt []int64
}

type myStruct struct {
	Num   int    `json:"num_json" orm:"column:num_orm"`
	Desc  string `json:"desc_json" orm:"column:desc_orm"`
	Child secStruct
}

func main() {
	s := myStruct{}
	typeS := reflect.TypeOf(s)
	// 成员数量
	// NumField:3
	fmt.Printf("NumField:%v \n", typeS.NumField())
	// 每个成员的信息 包括名称、类型、Tag
	for i := 0; i < typeS.NumField(); i++ {
		// 通过下标访问成员
		// Field(0):{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}
		// Field(1):{Name:Desc PkgPath: Type:string Tag:json:"desc_json" orm:"column:desc_orm" Offset:8 Index:[1] Anonymous:false}
		// Field(2):{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}
		fmt.Printf("Field(%v):%+v\n", i, typeS.Field(i))
	}
	// 通过名称访问成员
	field, ok := typeS.FieldByName("Num")
	// FieldByName("Num") ok:true field:{Name:Num PkgPath: Type:int Tag:json:"num_json" orm:"column:num_orm" Offset:0 Index:[0] Anonymous:false}
	fmt.Printf("FieldByName(\"Num\") ok:%v field:%+v\n", ok, field)
	// 获取tag值
	// json tag val:num_json
	fmt.Printf("json tag val:%+v\n", field.Tag.Get("json"))
	if value, ok := field.Tag.Lookup("orm"); ok {
		// rm tag val:column:num_orm
		fmt.Printf("orm tag val:%+v\n", value)
	}
	// 获取嵌套结构体的字段
	// Cnt field:{Name:Child PkgPath: Type:main.secStruct Tag: Offset:24 Index:[2] Anonymous:false}
	fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2}))
	// Cnt field:{Name:Cnt PkgPath: Type:[]int64 Tag: Offset:0 Index:[0] Anonymous:false}
	fmt.Printf("Cnt field:%+v\n", typeS.FieldByIndex([]int{2, 0}))
}

3、reflect.Value

通过 reflect.ValueOf 获取变量值、值类型,种类为 Array, Chan, Map, Slice 或 String,可通过 Len() 获取长度。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	b := int32(1)
	valueB := reflect.ValueOf(b)
	// reflect.TypeOf(b):1 Kind:int32
	fmt.Printf("reflect.TypeOf(b):%v Kind:%v\n", valueB, valueB.Kind())
	s := "abcdefg"
	valueS := reflect.ValueOf(s)
	// reflect.TypeOf(s):abcdefg Kind:string Len:7
	fmt.Printf("reflect.TypeOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
}

3.1 结构体的成员的值

和 2.3 结构体成员类型获取结构体成员类型类似,reflect 提供了 NumField 获取成员数量,Field 通过下标访问成

员的值。

package main

import (
	"fmt"
	"reflect"
)

type secStruct struct {
	Cnt []int64
}

type myStruct struct {
	Num   int    `json:"num_json" orm:"column:num_orm"`
	Desc  string `json:"desc_json" orm:"column:desc_orm"`
	Child secStruct
}

func main() {
	s := myStruct{
		Num:   100,
		Desc:  "desc",
		Child: secStruct{[]int64{1, 2, 3}},
	}
	valueS := reflect.ValueOf(s)
	// 成员数量
	// NumField:3
	fmt.Printf("NumField:%v \n", valueS.NumField())
	// 每个成员的值
	for i := 0; i < valueS.NumField(); i++ {
		// 通过下标访问成员
		// value(0):100
		// value(1):desc
		// value(2):{Cnt:[1 2 3]}
		fmt.Printf("value(%v):%+v\n", i, valueS.Field(i))
	}
	// 通过名称访问成员
	value := valueS.FieldByName("Num")
	// FieldByName("Num") value:100
	fmt.Printf("FieldByName(\"Num\") value:%v\n", value)
	// 获取嵌套结构体的字段
	// Cnt field:[1 2 3]
	fmt.Printf("Cnt field:%+v\n", valueS.FieldByIndex([]int{2, 0}))
}

3.2 遍历array、slice

通过 func (v Value) Index(i int) Value 可以通过下标来访问 Array,Slice,或者 String 各个元素的值。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	s := []int64{1, 2, 3, 4, 5, 6}
	valueS := reflect.ValueOf(s)
	// ValueOf(s):[1 2 3 4 5 6] Kind:slice Len:6
	fmt.Printf("ValueOf(s):%v Kind:%v Len:%v\n", valueS, valueS.Kind(), valueS.Len())
	for i := 0; i < valueS.Len(); i++ {
		// valueS.Index(0):1
		// valueS.Index(1):2
		// valueS.Index(2):3
		// valueS.Index(3):4
		// valueS.Index(4):5
		// valueS.Index(5):6
		fmt.Printf("valueS.Index(%v):%v\n", i, valueS.Index(i))
	}
}

3.3 遍历map

reflect 有两种方法遍历 map

  • 通过迭代器 MapIter 遍历 map

  • 先获取 map 的所有 key,再通过 key 获取对应的 value

package main

import (
	"fmt"
	"reflect"
)

func main() {
	m := map[int]string{
		1: "1",
		2: "2",
		3: "3",
	}
	valueM := reflect.ValueOf(m)
	// 迭代器访问
	iter := valueM.MapRange()
	for iter.Next() {
		// key:1 val:1
		// key:2 val:2
		// key:3 val:3
		fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value())
	}
	// ------
	fmt.Println("------")
	// 通过key访问
	keys := valueM.MapKeys()
	for i := 0; i < len(keys); i++ {
		// key:1 val:1
		// key:2 val:2
		// key:3 val:3
		fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i]))
	}
}

4、反射的三大定律

反射的两个基础函数定义:

  • 获取类型 func TypeOf(i any) Type
  • 获取值 func ValueOf(i any) Value

其中,any 是 interface{} 的别名。

interface{} 是不包含任何方法签名的空接口,任何类型都实现了空接口。

因此,interface{} 可以承载任何变量的 (value, concrete type)信息。

4.1 从interface到反射对象

interface{} 承载变量的 (value, concrete type) 信息,通过反射暴露方法来访问 interface{} 的值和类型。

可以简单理解为 interface{} 的值和信息传递到 reflect.Type 和 reflect.Value,方便获取。

4.2 从反射对象到interface

可以通过函数 func (v Value) Interface() (i any) 将反射对象转换为 interface{},是 func ValueOf(i any) Value的反

向操作。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := int32(10)
	valueA := reflect.ValueOf(a)
	// ValueOf(a):10
	fmt.Printf("ValueOf(a):%v\n", valueA)
	// Interface():10
	fmt.Printf("Interface():%v\n", valueA.Interface())
	ai, ok := valueA.Interface().(int32)
	// ok:true val:10
	fmt.Printf("ok:%v val:%v\n", ok, ai)
}

4.3 通过反射修改对象,该对象值必须是可修改的

reflect提供 func (v Value) CanSet() bool 判断对象值是否修改,通过 func (v Value) Set(x Value) 修改对象值。文章来源地址https://www.toymoban.com/news/detail-608378.html

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := int32(10)
	valueA := reflect.ValueOf(a)
	// valueA :false
	fmt.Printf("valueA :%v\n", valueA.CanSet())
	b := int32(100)
	valuePtrB := reflect.ValueOf(&b)
	// valuePtrB:false Elem:true
	fmt.Printf("valuePtrB:%v Elem:%v\n", valuePtrB.CanSet(), valuePtrB.Elem().CanSet())
	valuePtrB.Elem().Set(reflect.ValueOf(int32(200)))
	// b:200 Elem:200
	fmt.Printf("b:%v Elem:%v\n", b, valuePtrB.Elem())
}

到了这里,关于Go语言通过反射获取各种类型变量的值的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 利用反射获取websocket,session字段的值

    首先利用反射获取方法字段,然后取得字段的值 获取的字段值 webSocketContainer :WebSocket容器,用于管理WebSocket连接。 requestUri :请求的URI,即WebSocket的URL。 requestParameterMap :请求的参数映射,包含从URL中提取的参数。 queryString :查询字符串,即URL中的参数部分。 userPrincipal

    2024年01月17日
    浏览(42)
  • golang反射获取结构体的值和修改值

    封装好就可以直接在service层使用了【自己手动复制粘贴】 或者嫌麻烦就放到【代码生成器】中,这样就不用每次都去写这个查询了

    2024年02月15日
    浏览(42)
  • Go语言变量使用指南:声明、类型转换与字符串操作

    深入了解Go语言中变量的声明方式、字符和布尔类型的细节、字符串的不可变性以及基本数据类型之间的转换规则。

    2024年02月10日
    浏览(86)
  • 【Go进阶】详解方法的值类型和指针类型区别

    目录 值类型和指针类型 方法也有值类型的方法和指针类型的区别 ,也就是以下两种receiver: setname()方法中是值类型的receiver,setage()方法中是指针类型的receiver。它们是有区别的。 首先,setage()方法的p是一个指针类型的person实例,所以方法体中的 p.age 实际上等价于 (*p).age 。

    2023年04月10日
    浏览(38)
  • Java中的反射(通过反射获取类的结构、invoke方法、获取注解)

    创建运行时类的对象是反射机制应用最多的地方。创建运行时类的对象有两种方式: 方式1:直接调用Class对象的newInstance()方法 要求: 1)类必须有一个无参数的构造器。 2)类的构造器的访问权限需要足够。 方式一的步骤 : 1)获取该类型的Class对象 2)调用Class对象的 new

    2024年02月04日
    浏览(49)
  • go-zero 如何在任意地方获取yaml中的值

    1、config配置文件中新增全局变量 2、main函数所在的入口文件为其赋值 3、然后在想要使用的地方直接使用就可以了。 比如使用yaml中配置的JWT认证的key

    2024年01月20日
    浏览(36)
  • Go语言之反射(反射的简单使用,原理)

    1.什么是反射 Go语言中,反射的机制就是在运行的时候,可以获取到其变量的类型和值,且可以对其类型和值进行检查,对其值进行修改。 即在不知道具体的类型的情况下,可以用反射机制来查看变量类型、更新变量的值。 Go中反射主要涉及到两个概念:Type和Value。对所有的

    2023年04月25日
    浏览(35)
  • Java反射获取属性名、属性类型、属性值

    反射、泛型、JDBC 等基础结合起来很多时候可以使代码功能更加强大,适合更多的场景使用 一个通过反射获得属性名、属性类型、属性值的示例: 执行,输出:

    2024年02月12日
    浏览(48)
  • Golang 中的反射,并用来获取数据类型

    Go语言提供了一种机制在运行中获取某个变量的类型,获取或修改变量的值,调用变量的方法。 示例代码如下 通过 reflect.Value 判断变量类型,并转换成 string 。 输出结果: 输出结果:

    2024年01月22日
    浏览(51)
  • Go语言反射

    #反射reflect 先看官方Doc中Rob Pike给出的关于反射的定义: 维基百科中的定义: 不同语言的反射模型不尽相同,有些语言还不支持反射。《Go 语言圣经》中是这样定义反射的: Go 语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道

    2024年02月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包