Go 反射
本文最后更新于:2 小时前
有时候我们需要写一个函数,这个函数有能力统一处理各种值类型。而这些类型可能无法共享同一个接口,也可能布局位置,也有课呢呢个这个类型在我们设计函数时候还不存咋,这个时候,我们就可以使用反射
1 空接口可以存储任意类型的变量,那我们如何知道这个接口保存的数据的类型是什么?值是什么?
- 可以使用类型断言
- 可以使用反射实现,也就是在程序运行时动态的获取一个变量的类型信息和值信息
2 把结构体序列化成json 字符串,自定义结构体Tab标签的时候 就用到了反射
反射的介绍
反射是指在程序运行期间对程序本身进行访问和修改的能力,正常情况程序在编译时候,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时候,程序无法获取自身的信息,支持反射的语言可以在程序编译期间将变量的发射信息。如字段信息,类型信息,结构体信息,等 整合到可执行文件中,并给程序员提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改他们
反射实现的功能
- 反射可以在程序运行期间动态的获取变量的各种信息, 比如变量的类型
- 如果是结构体,通过反射还可以获取结构体本身的信息, 比如结构体的字段结构体的方法
- 通过反射, 可以修改变量的值,可以调用关联的方法
Go 语言中的变量是分为俩部分的
- 类型信息:预先定义好的元信息
- 值信息: 程序运行过程中可动态变化的
在Go语言中的反射机制中,任何接口值都是由一个具体类型和具体类型的值俩部分组成的
在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type 和 reflect.Value
并且reflect包提供了reflect.TypeOf 和 reflect.ValueOf 俩个重要函数来获取任意对象的Value和Type
package main
import (
"fmt"
"reflect"
)
type myInt int
type Stu struct {
Name string
Age int
}
func reflectFunc(x interface{}) {
v := reflect.TypeOf(x)
fmt.Println(v)
}
func main() {
a := 10
b := 23.4
c := true
d := "hello golang"
reflectFunc(a)
reflectFunc(b)
reflectFunc(c)
reflectFunc(d)
var e myInt = 1
reflectFunc(e)
var s1 = Stu{
Name: "bds",
Age: 19,
}
reflectFunc(s1)
}
/*package main
import (
"fmt"
"reflect"
)
type myInt int
type Stu struct {
Name string
Age int
}
func reflectFunc(x interface{}) {
v := reflect.TypeOf(x)
fmt.Println(v)
}
func main() {
a := 10
b := 23.4
c := true
d := "hello golang"
reflectFunc(a)
reflectFunc(b)
reflectFunc(c)
reflectFunc(d)
var e myInt = 1
reflectFunc(e)
var s1 = Stu{
Name: "bds",
Age: 19,
}
reflectFunc(s1)
}
*/
type Name 和type Kind
在反射中关于类型划分为俩种: 类型(Type) 和 种类(Kind)。因为在Go语言中我们可以使用Type 关键字构造很多自定义类型,而种类(Kind) 就是底层数据类型,但是在反射中 当需要却分指针,结构体等大品种的类型时,就会用到种类(Kind)
举个列子我们定义了俩个指针类型和俩个结构体类型 通过反射查看他们的类型和种类。
Go语言中的反射 像数组 切牌呢 Map 指针等类型的变量 他们.Name()都是返回空
package main
import (
"fmt"
"reflect"
)
type myInt int
type Stu struct {
Name string
Age int
}
func reflectFunc(x interface{}) {
v := reflect.TypeOf(x)
fmt.Printf("类型:%v类型名称:%v 底层类型种类:%v\n", v, v.Name(), v.Kind())
}
func main() {
a := 10
b := 23.4
c := true
d := "hello golang"
reflectFunc(a)
reflectFunc(b)
reflectFunc(c)
reflectFunc(d)
var e myInt = 1
reflectFunc(e)
var s1 = Stu{
Name: "bds",
Age: 19,
}
reflectFunc(s1)
var p1 = 1
reflectFunc(&p1)
var i = [3]int{1, 2, 3}
var j = []int{11, 22, 33}
reflectFunc(i)
reflectFunc(j)
}
/*
类型:int类型名称:int 底层类型种类:int
类型:float64类型名称:float64 底层类型种类:float64
类型:bool类型名称:bool 底层类型种类:bool
类型:string类型名称:string 底层类型种类:string
类型:main.myInt类型名称:myInt 底层类型种类:int
类型:main.Stu类型名称:Stu 底层类型种类:struct
类型:*int类型名称: 底层类型种类:ptr
类型:[3]int类型名称: 底层类型种类:array
类型:[]int类型名称: 底层类型种类:slice
*/
通过ValueOf判断类型
package main
import (
"fmt"
"reflect"
)
func reflectFunc(x interface{}) {
//我们直接进行想加 会报错
//c := x + 10 // invalid operation: x + 10 (mismatched types interface {} and int
//通过类型断言的方式 求值
b, _ := x.(int)
c := b + 10
fmt.Println(c)
//通过反射的方式 求值
v := reflect.ValueOf(x)
c1 := v.Int() + 11 //通过v.Int()获取原始值, 还支持其他类型 类型名称首字母大写
fmt.Println(c1)
}
func main() {
var a = 10
reflectFunc(a)
}
通过ValueOf 和Kind 判断类型 获取变量的值
package main
import (
"fmt"
"reflect"
)
func reflectFunc(x interface{}) {
v := reflect.ValueOf(x)
kind := v.Kind()
switch kind {
case reflect.String:
fmt.Printf("string类型原始值:%v\n", v.String())
case reflect.Int:
fmt.Printf("int类型的原始值:%v\n", v.Int()+10)
default:
fmt.Printf("没有匹配")
}
}
func main() {
var a = 10
reflectFunc(a)
b := "你好 golang"
reflectFunc(b)
}
通过反射设置变量的值
package main
import (
"fmt"
"reflect"
)
func setFunc(x interface{}) {
v := reflect.ValueOf(x)
//如果是指针类型的变量 获取底层数据类型需要加Elem方法
kind := v.Elem().Kind()
if kind == reflect.Int {
//设置值的时候 也需要加上Elem
v.Elem().SetInt(100)
}
}
func main() {
var a int = 10
//修改值的话 我们这里传入指针类型变量
setFunc(&a)
fmt.Println(a)
}
结构体反射
任意值通过reflect.TypeOf() 获取反射对象信息后,如果他的类型是结构体,可以通过反射值对象(reflect.Type)的NumFiled()和Field()方法获的结构体成员的详细信息
package main
import (
"fmt"
"reflect"
)
type Stu1 struct {
Name string `json:"name1"`
Age int `json:"age1"`
Sex string `json:"sex1"`
}
func (s Stu1) GetInfo() string {
str := fmt.Sprintf("姓名:%v, 年龄:%v,性别: %v", s.Name, s.Age, s.Sex)
return str
}
func (s *Stu1) SetInfo(name string, age int, sex string) {
s.Name = name
s.Age = age
s.Sex = sex
}
func (s Stu1) Print() {
fmt.Println("我是一个Print打印方法")
}
func reflectStruct(s interface{}) {
//判断传入的参数是不是结构体
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
fmt.Println(t.Kind())
if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体类型")
return
}
//通过类型变量里面的Field可以获取结构体的字段
field0 := t.Field(0)
fmt.Printf("%#v\n", field0)
fmt.Printf("字段名称:%v\n", field0.Name)
fmt.Printf("字段类型:%v\n", field0.Type)
fmt.Printf("tag标签:%v\n", field0.Tag.Get("json"))
//通过类型变量里面的FieldByName可以获取结构体的字段
fmt.Println("--------")
field1, ok := t.FieldByName("Age")
if ok {
fmt.Println("字段名称:", field1.Name)
fmt.Println("字段类型:", field1.Type)
fmt.Println("字段Tag:", field1.Tag.Get("json"))
}
//通过类型变量里面的NumField获取该结构体有几个字段
fmt.Println("--------")
var count = t.NumField()
fmt.Printf("结构体有 %v 个属性\n", count)
//通过值变量 获取结构体属性对应的值
fmt.Println("--------")
fmt.Println(v.FieldByName("Name"))
fmt.Println(v.FieldByName("Age"))
fmt.Println("--------")
for i := 0; i < count; i++ {
fmt.Printf("属性名称:%v,属性的值:%v,属性类型:%v,属性Tag值:%v\n", t.Field(i).Name, v.Field(i), t.Field(i).Type, t.Field(i).Tag.Get("json"))
}
}
func pStruct(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
fmt.Println(t.Kind())
if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体")
return
}
//通过类型变量里面的Method方法可以获取结构体的方法
fmt.Println("--------")
m0 := t.Method(0)
fmt.Println(m0.Name)
fmt.Println(m0.Type)
//通过类型变量获取这个结构体有多少方法
fmt.Println("--------")
m1, ok := t.MethodByName("Print")
if ok {
fmt.Println(m1.Name)
fmt.Println(m1.Type)
} else {
fmt.Println("none")
}
//通过<<值变量>> 执行方法 (注意需要使用值变量,并且要注意参数)v.Method(0).Call(nil)
fmt.Println("--------")
v.Method(1).Call(nil)
info := v.MethodByName("GetInfo").Call(nil)
fmt.Println(info)
//执行方法传入参数,(注意需要值变量,并且要注意参数,接收的参数是[]reflect.Value的切片)
fmt.Println("--------")
var params []reflect.Value
params = append(params, reflect.ValueOf("李四"))
params = append(params, reflect.ValueOf(19))
params = append(params, reflect.ValueOf("girl"))
v.MethodByName("SetInfo").Call(params) //给方法函数 传入参数
info2 := v.MethodByName("GetInfo").Call(nil)
//再次通过GetInfo 方法打印 结构体 看看是上面修改值 是否 已经改变
fmt.Println(info2)
//获取方法的数量
fmt.Println("--------")
fmt.Printf("方法数量:%v\n", t.NumMethod())
}
func changeStruct(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
if t.Kind() != reflect.Ptr {
fmt.Println("传入的不是结构体指针")
return
} else if t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体")
return
}
fmt.Println("----change struct-----")
//修改结构体属性的值 (通过值变量)
name := v.Elem().FieldByName("Name")
name.SetString("李武")
age := v.Elem().FieldByName("Age")
age.SetInt(20)
}
func main() {
var s1 = Stu1{
Name: "bds",
Age: 18,
Sex: "man",
}
fmt.Println("--- main1 -----")
reflectStruct(s1)
fmt.Println("--- main2 -----")
pStruct(&s1) //因为要修改结构体 ,所以要传入指针类型
fmt.Println("--- main3 -----")
fmt.Println(s1)
fmt.Println("--- main4 -----")
changeStruct(&s1)
fmt.Println(s1) //再次打印 看值 是否修改成功
}
/*
--- main1 -----
struct
reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x10d1760), Tag:"json:\"name1\"", Offset:0x0, Index:[]int{0}, Anonymous:false}
字段名称:Name
字段类型:string
tag标签:name1
--------
字段名称: Age
字段类型: int
字段Tag: age1
--------
结构体有 3 个属性
--------
bds
18
--------
属性名称:Name,属性的值:bds,属性类型:string,属性Tag值:name1
属性名称:Age,属性的值:18,属性类型:int,属性Tag值:age1
属性名称:Sex,属性的值:man,属性类型:string,属性Tag值:sex1
--- main2 -----
ptr
--------
GetInfo
func(*main.Stu1) string
--------
Print
func(*main.Stu1)
--------
我是一个Print打印方法
[姓名:bds, 年龄:18,性别: man]
--------
[姓名:李四, 年龄:19,性别: girl]
--------
方法数量:3
--- main3 -----
{李四 19 girl}
--- main4 -----
----change struct-----
{李武 20 girl}
*/
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!