go 的反射 reflect
Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。
grpc 的 golang 版本也是靠反射实现的。
reflect 的基本功能
- reflect.TypeOf 返回类型 reflect.Type
- reflect.ValueOf 返回值 reflect.Value
- 可以从reflect.Value 中获取类型
- 通过Kind 来判断类型
先看第一个例子
一 、 反射的基本用法
ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0
TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil
// 反射的基础用法
func base00() {
var num = 1.2345
fmt.Println("type: ",reflect.TypeOf(num))
fmt.Println("value: ",reflect.ValueOf(num))
}
运行结果:
type : float64
value : 1.234
二 、 反射来判断参数类型
以下两种方法可以判断 传入参数的类型,动态进行判断。
func base01(v interface{}) {
t := reflect.TypeOf(v)
switch t.Kind() {
case reflect.Float32,reflect.Float64:
fmt.Println("float")
case reflect.Int16,reflect.Int32,reflect.Int64:
fmt.Println("int")
case reflect.String:
fmt.Println("string")
default:
fmt.Println("none")
}
switch v.(type) { // 方法二
case float64,float32:
fmt.Println("float")
case int32,int64:
fmt.Println("int")
case string:
fmt.Println("none")
default:
fmt.Println("none")
}
}
三 、 通过interface 获取到真实的值
当执行reflect.ValueOf(interface)之后,就得到了一个类型为”relfect.Value”变量,可以通过它本身的Interface()方法获得接口变量的真实内容,然后可以通过类型判断进行转换,转换为原有真实类型。
其对类型要求是什么严格的。
// 已知原有类型进行强制类型转换
func base02() {
var num = 1.2345
pointer := reflect.ValueOf(&num)
value := reflect.ValueOf(num)
// 转换类型不匹配会直接 panic
convertPointer := pointer.Interface().(*float64)
convertValue := value.Interface().(float64)
fmt.Println(convertPointer)
fmt.Println(convertValue)
}
执行结果:
0xc000098010
1.2345
四 、通过反射来遍历一个结构体,其方法,其feild
- 先获取interface的reflect.Type,然后通过NumField进行遍历
- 再通过reflect.Type的Field获取其Field
- 最后通过Field的Interface()得到对应的value
type User struct {
Id int
Name string
Work Worker
}
type Worker struct {
Id int
Occupation string
}
func (u User) ReflectCallFunc() {
fmt.Printf("My Id :%d ,My Name :%s ,My Occupation :%s",u.Id,u.Name,u.Work.Occupation)
}
func (u User) ReflectCallFuncHasArgs(foo int) {
fmt.Printf("This is number %d \n", foo)
}
// 遍历类型探测其值
func base03(input interface{}) {
getType := reflect.TypeOf(input)
fmt.Println("get type is: ",getType.Name()) // get type is: User
getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is ", getValue) // get all Fields is {1 ZhangSan {2 Programmer}}
// 对字段进行遍历 获取方法的字段
for i := 0;i < getType.NumField();i++ {
field := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Printf("%s: %v = %v \n",field.Name,field.Type,value)
}
/*
Id: int = 1
Name: string = ZhangSan
Work: main.Worker = {2 Programmer}
*/
// 获取方法
for i := 0;i < getType.NumMethod();i++ {
m := getType.Method(i)
fmt.Printf("%s : %v\n",m.Name,m.Type)
}
/*
ReflectCallFunc : func(main.User)
ReflectCallFuncHasArgs : func(main.User, int)
*/
}
五 、 通过反射来重新赋值
reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是“addressable”的。
// 通过反射设置变量的值
func base04() {
var num = 1.234
fmt.Println("old value of pointer: ",num) // old value of pointer: 1.234
// 获取其指针(必须是指针)
pointer := reflect.ValueOf(&num)
newValue := pointer.Elem()
fmt.Println("type of pointer:",newValue.Type()) // type of pointer: float64
fmt.Println("settability of pointer",newValue.CanSet()) // settability of pointer true
// 重新赋值
newValue.SetFloat(3.1415)
fmt.Println("new value of pointer: ",num) // old value of pointer: 1.234
}
六、通过反射调用方法
我们可以利用反射来编写灵活的代码
按名字访问结构成员
reflect.ValueOf(e).FieldByName("Name")
按名字访问结构方法
reflect.ValueOf(e).MethodByName("FuncName").Call([]reflect.Value{reflect.ValueOf(1)})
// 通过反射调用方法
func base05(input interface{}) {
getValue := reflect.ValueOf(input)
// 有参数调用
methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
args := []reflect.Value{reflect.ValueOf(2)}
methodValue.Call(args)
// 无参数调用
methodValue = getValue.MethodByName("ReflectCallFunc")
args = make([]reflect.Value,0)
methodValue.Call(args)
}
七、判断一个参数是不是指针
kind 可以进行类型判断, 加上 string() 方法,就可以将类型进行字符串打印。
// 最简单调用
func base06(input interface{}) {
k := reflect.TypeOf(input).Kind().String()
fmt.Println(k)
t := reflect.TypeOf(input).Elem()
v := reflect.ValueOf(input).Elem()
fmt.Println(t)
fmt.Println(v)
}
执行结果
ptr
main.User
{1 ZhangSan {2 Programmer}}
八、记一类特殊的反射
在对json数据的序列化,反序列化的时候,经常会看到如下的代码
type Config struct {
Id int `bson:"id" json:"id"`
Created time.Time `bson:"created" json:"created"`
Updated time.Time `bson:"updated" json:"updated"`
Creator string `bson:"creator" json:"creator"`
Updater string `bson:"updater" json:"updater"`
}
struct tag ,在struct field 的后面有一个"format:normal"格式的 tag。其实在解析时,这些东西就是用反射去解析的。
如下面的示例
type Employee struct {
EmployeeID string
Name string `format:"normal"`
Age int
}
func base07() {
e := &Employee{"1", "Mike", 30}
//按名字获取成员
fmt.Printf("Name: value(%[1]v), Type(%[1]T) \n", reflect.ValueOf(*e).FieldByName("Name"))
if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
fmt.Println("Failed to get 'Name' field.")
} else {
// 获取 tag中值
fmt.Println("Tag:format", nameField.Tag.Get("format"))
}
}
执行结果:
Name: value(Mike), Type(reflect.Value)
Tag:format normal