go语言的反射机制,是程序在运行期间可以对程序本身的一些变量或者结构体等信息进行访问和修改,可以让函数接口的类型更加多元化。比如,通过给函数定义interface{}类型的参数,在函数内部利用反射针对不同类型参数进行不同处理。
反射的调用是使用reflect包,reflect 包的两种类型 Type 和 Value,这两种类型使访问接口内的数据成为可能,它们对应两个简单的方法,分别是 reflect.TypeOf 和 reflect.ValueOf,分别用来读取接口变量的 reflect.Type 和 reflect.Value 部分。对应的还有Kind方法,是获取变量的基础种类。Elem方法,是获取指针变量所指的值就是取值操作。CanSet方法,是表示变量是否可以被修改,如是指针传递过来的变量,就可以修改;如是值传递的,就不能修改。SetInt,SetFloat,SetString就是修改不同类型变量值的方法。对于结构体类型反射,NumField就是返回结构体字段的数量,Field(i)就是获取第i个字段的值,还有tag标签的获取使用。
下面是具体使用范例:
package main
import (
"fmt"
"reflect"
)
type Bird struct {
Swing int `id:"iloveyou" num:"222"` //tag标签信息
Name string
}
func PriSet(i interface{}) {
vl := reflect.ValueOf(i)
//nii := 200
if vl.Kind() == reflect.Ptr { //如是指针类型,先取值
vl = vl.Elem() //Elem方法就是取值操作
}
vlt := vl.Type() //获取具体类型
if vl.Kind() != reflect.Struct {
fmt.Println("类型:", vlt, " 名字:", vlt.Name(), " 数值:", vl)
//如是整数种类,又可以修改,CanSet属性为true,则进行修改。
if vl.Kind() == reflect.Int || vl.Kind() == reflect.Int64 && vl.CanSet() {
vl.SetInt(100)
//reflect.New(vlt)是创建了一个vlt类型的指针变量。
ni := reflect.New(vlt)
//创建后的ni是reflect.Value对象,需要通过ValueOf来赋值
ni = reflect.ValueOf(222)
fmt.Println("修改后int数值:", vl, "新建数值:", ni)
}
//如是浮点数种类,又可以修改,CanSet属性为true,则进行修改。
if vl.Kind() == reflect.Float32 || vl.Kind() == reflect.Float64 && vl.CanSet() {
vl.SetFloat(100.11)
fmt.Println("修改后float数值:", vl)
}
} else {
fmt.Println("结构体类型:", vlt, " 名字:", vlt.Name())
for i := 0; i < vl.NumField(); i++ {
//vl是具体的值,这里vl.Field(i)就是枚举出每一个成员的值
//vlt是获取的类型,vlt.Field(i).Name和.Type是枚举出每一个成员的名字和类型
//也可以使用FieldByName,从指定的成员名获取具体值,vlret:= vl.FieldByName(vlt.Field(i).Name)
fmt.Println("成员名:", vlt.Field(i).Name, " 类型:", vlt.Field(i).Type, " 数值:", vl.Field(i))
//如是字符串种类,又可以修改,CanSet属性为true,则进行修改。
if vl.Field(i).Kind() == reflect.String && vl.Field(i).CanSet() {
vl.Field(i).SetString("new str")
fmt.Println("string成员修改后数值:", vl.Field(i))
}
//而tag标签信息都是静态的,无须实例化结构体,通过类型vlt可以获取到。
//这句vlt.Field(i).Tag.Lookup("id")就是枚举出每一个成员的tag标签,看里面是否有id这个key,并返回它的value值
if idtag, b := vlt.Field(i).Tag.Lookup("id"); b {
fmt.Println("tag id=", idtag)
}
if numtag, b := vlt.Field(i).Tag.Lookup("num"); b {
fmt.Println("tag num=", numtag)
}
}
}
}
func main() {
var fa float64 = 3.14
var ia int64 = 5
bd := Bird{100, "andy"}
ns := struct { //定义一个无名结构体
id int
addr string
}{2, "aa"}
PriSet(fa)
PriSet(&ia) //把变量地址传递,所以可以修改。
PriSet(&bd) //把结构体地址传递,所以可以修改。
fmt.Println(bd) //打印出修改后的结构体变量
PriSet(ns)
}