一、反射的基础
1.什么是反射
- Go语言中,反射的机制就是在运行的时候,可以获取到其变量的类型和值,且可以对其类型和值进行检查,对其值进行修改。
- 即在不知道具体的类型的情况下,可以用反射机制来查看变量类型、更新变量的值。
- Go中反射主要涉及到两个概念:Type和Value。对所有的接口进行反射时,都可以得到一个包含Type和Value的信息结构,Type是反射的这个变量本身的类型信息,Value是反射的这个变量本身的值信息。
2.反射的使用
(1) 获取到变量类型
- 方法:reflect.TypeOf(x)
- 作用:获取变量x的类型信息
结果:package main import ( "fmt" "reflect" ) func main(){ x := 3.1415 y := 3 z := "sheena" u := true fmt.Println("x的type为:", reflect.TypeOf(x)) fmt.Println("y的type为:", reflect.TypeOf(y)) fmt.Println("z的type为:", reflect.TypeOf(z)) fmt.Println("u的type为:", reflect.TypeOf(u)) }
(2) 进行变量类型判断
- 方法:xtype := reflect.TypeOf(x) xkind := xtype.Kind() if xkind == reflect.Type
- 作用:先调用TypeOf方法获取到x的类型信息,再获取到类型信息中的类型,与类型进行判断
结果:package main import ( "fmt" "reflect" ) func main(){ x := 3.1415 y := 3 z := "sheena" u := true //获取变量的类型 xtype := reflect.TypeOf(x) ytype := reflect.TypeOf(y) ztype := reflect.TypeOf(z) utype := reflect.TypeOf(u) //Kind()方法的返回结果主要是用来进行类型判断的 xkind := xtype.Kind() ykind := ytype.Kind() zkind := ztype.Kind() ukind := utype.Kind() //对x进行类型判断 if xkind == reflect.String{ fmt.Println("x的type是string") }else if xkind == reflect.Float64{ fmt.Println("x的type是float64") } //对y进行类型判断 if ykind == reflect.Float64{ fmt.Println("y的type是Float64") }else if ykind == reflect.Int{ fmt.Println("y的type是int") } //对z进行类型判断 if zkind == reflect.Float64{ fmt.Println("z的type是Float64") }else if zkind == reflect.String{ fmt.Println("z的type是string") } //对u进行类型判断 if ukind == reflect.Bool{ fmt.Println("u的type是bool") }else if ukind == reflect.String{ fmt.Println("u的type是string") } }
(3) 获取到变量值
- 方法:reflect.ValueOf(x)
- 作用:获取x变量的值
结果:package main import ( "fmt" "reflect" ) func main(){ x := 3.1415 y := 3 z := "sheena" u := true fmt.Println("x的value为:", reflect.ValueOf(x)) fmt.Println("y的value为:", reflect.ValueOf(y)) fmt.Println("z的value为:", reflect.ValueOf(z)) fmt.Println("u的value为:", reflect.ValueOf(u)) }
(4) 修改变量值
- 方法一:使用具体类型的值的设置方法,如SetString()、SetFloat64()等(可直接接收具体要修改的值)
结果:package main import ( "fmt" "reflect" ) func main(){ x := 3.1415 y := 3 z := "sheena" u := true fmt.Println("x修改前的value为:", reflect.ValueOf(x)) fmt.Println("y修改前的value为:", reflect.ValueOf(y)) fmt.Println("z修改前的value为:", reflect.ValueOf(z)) fmt.Println("u修改前的value为:", reflect.ValueOf(u)) //通过反射传入变量x的地址,并且通过Ele rex := reflect.ValueOf(&x).Elem() rey := reflect.ValueOf(&y).Elem() rez := reflect.ValueOf(&z).Elem() reu := reflect.ValueOf(&u).Elem() //判断是否可以修改变量x的值,若可以,则用SetFLoat64()方法进行修改 if rex.CanSet(){ rex.SetFloat(61.23466) fmt.Println("x修改后的value为:", reflect.ValueOf(x)) }else { fmt.Println("该变量不能修改") } if rey.CanSet(){ rey.SetInt(10000) fmt.Println("y修改后的value为:", reflect.ValueOf(y)) }else { fmt.Println("该变量不能修改") } if rez.CanSet(){ rez.SetString("hello world") fmt.Println("z修改后的value为:", reflect.ValueOf(z)) }else{ fmt.Println("该变量不能修改") } if reu.CanSet(){ reu.SetBool(false) fmt.Println("u修改后的value为:", reflect.ValueOf(u)) }else { fmt.Println("该变量不能修改") } }
- 方法二:是用Set方法直接设置值(注意set方法接收的是ValueOf的返回值)
结果:package main import ( "fmt" "reflect" ) func main(){ x := 3.1415 y := 3 z := "sheena" u := true fmt.Println("x修改前的value为:", reflect.ValueOf(x)) fmt.Println("y修改前的value为:", reflect.ValueOf(y)) fmt.Println("z修改前的value为:", reflect.ValueOf(z)) fmt.Println("u修改前的value为:", reflect.ValueOf(u)) //通过反射传入变量x的地址,并且通过Ele rex := reflect.ValueOf(&x).Elem() rey := reflect.ValueOf(&y).Elem() rez := reflect.ValueOf(&z).Elem() reu := reflect.ValueOf(&u).Elem() //判断是否可以修改变量x的值,若可以,则用Set()方法进行修改 if rex.CanSet(){ ax := reflect.ValueOf(61.23466) // 使用Set方法修改值,Set方法接收的是ValueOf的返回值 rex.Set(ax) fmt.Println("x修改后的value为:", reflect.ValueOf(x)) }else { fmt.Println("该变量不能修改") } if rey.CanSet(){ ay := reflect.ValueOf(10000)// 使用Set方法修改值,Set方法接收的是ValueOf的返回值 rey.Set(ay) fmt.Println("y修改后的value为:", reflect.ValueOf(y)) }else { fmt.Println("该变量不能修改") } if rez.CanSet(){ az := reflect.ValueOf("hello world")// 使用Set方法修改值,Set方法接收的是ValueOf的返回值 rez.Set(az) fmt.Println("z修改后的value为:", reflect.ValueOf(z)) }else{ fmt.Println("该变量不能修改") } if reu.CanSet(){ au := reflect.ValueOf(false)// 使用Set方法修改值,Set方法接收的是ValueOf的返回值 reu.Set(au) fmt.Println("u修改后的value为:", reflect.ValueOf(u)) }else { fmt.Println("该变量不能修改") } }
(5) 获取变量的指针所指向的对象
- 方法:Elem()方法
- 作用:获取变量x的指针所指向的对象
结果:package main import ( "fmt" "reflect" ) func main(){ x := 3.1415 y := 3 z := "sheena" u := true //传入变量地址 px := reflect.ValueOf(&x) py := reflect.ValueOf(&y) pz := reflect.ValueOf(&z) pu := reflect.ValueOf(&u) fmt.Println("x的地址是", px) fmt.Println("y的地址是", py) fmt.Println("z的地址是", pz) fmt.Println("u的地址是", pu) //通过变量地址获取到变量的值 xe := px.Elem() ye := py.Elem() ze := pz.Elem() ue := pu.Elem() fmt.Println("x的值是", xe) fmt.Println("y的值是", ye) fmt.Println("z的值是", ze) fmt.Println("u的值是", ue) }
(6) 获取结构体变量的类型和值
package main
import (
"fmt"
"reflect"
)
type Stu struct{
Name string
Age int
Sex string
IsCan bool
}
func main(){
s1 := Stu{Name: "王一", Age: 18, Sex: "男", IsCan: false}
s2 := Stu{Name: "王二", Age: 19, Sex: "女", IsCan: true}
s3 := Stu{Name: "张三", Age: 20, Sex: "男", IsCan: false}
//反射获取结构体的类型和值
fmt.Println("s1的类型", reflect.TypeOf(s1))
fmt.Println("s1的值", reflect.ValueOf(s1))
fmt.Println("s2的类型", reflect.TypeOf(s2))
fmt.Println("s2的值", reflect.ValueOf(s2))
fmt.Println("s3的类型", reflect.TypeOf(s3))
fmt.Println("s3的值", reflect.ValueOf(s3))
fmt.Println("TypeOf()和Kind()方法输出的区别")
fmt.Println("TypeOf(s1):", reflect.TypeOf(s1))
s1tp := reflect.TypeOf(s1)
fmt.Println("Kind(s1):", s1tp.Kind())
}
结果:
二、反射的原理
1.反射如何获取类型信息
- reflect包中提供TypeOf接口用于获取一个变量的类型信息。它接收一个空接口类型的参数,并返回一个reflect.Type类型的返回值
- TypeOf接口:
func TypeOf(i interface{}) Type{ eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.type) } //Type接口提供了一系列方法 type Type interface{ Align() int //对齐边界 FieldAlign() int Method(int) Method MethodByName(string) (Method, bool) //方法 NumMethod() int //类型名称 Name() string PkgPath() string //包路径 Slize() uintptr String() string Kind() Kind Implements(u Type) bool //是否实现指定接口 AssginableTo(u Type) bool ConvertibleTo(u Type) bool Comparable() bool //是否可比较 }
2.反射如何修改变量值
- 用反射修改变量值,需要用到reflect.Value类型了,如下所示结构体,有三个元素,type字段存储反射变量的类型元数据指针;ptr字段存储数据地址;flag字段是一个位标识符,存储反射变量的描述信息,例如是否为指针,是否为方法或是否只读等等。
type Value struct { type *rtype ptr unsafe.Pointer flag } func ValueOf(i interface{}) Value{ if i == nill{ return Value() } escapes(i) return unpackEface(i) }
- 通常会有reflect.ValueOf方法来拿到反射类型的值,注意这里的参数i和TypeOf函数一样是个空接口类型,所以处理方式一样。(编译阶段会增加一个临时变量作为拷贝)