什么是反射?
反射提供了在运行时得到变量类型和值的能力.
为什么需要反射?
既然变量都是自己定义的,为什么需要在运行时得到类型和值的信息呢?
首先了解一个神奇的概念:空接口(empty interface).一个没有定义任何方法的接口就是空接口,也就是说任何类型的变量都可以转化成空接口类型.
The empty interface is extremely useful when we are declaring a function with unknown parameters and data types.
当定义一个函数时,他的参数类型没有办法固定下来,空接口将非常有用.
例如:
package main
import (
"fmt"
"reflect"
)
type vocation struct {
id int
title string
}
type employee struct {
id int
name string
}
func createQuery(p interface{}) {
t := reflect.TypeOf(p)
k := t.Kind()
fmt.Println("type is ", t)
fmt.Println("kind is ", k)
if k == reflect.Struct {
value := reflect.ValueOf(p)
numField := value.NumField()
for i := 0; i < numField; i++ {
fmt.Printf("field %d, type %T, value %v\n", i, value.Field(i), value.Field(i))
}
}
}
func main() {
var manager = vocation{id: 5, title: "manager"}
var xiaoHong = employee{id: 30, name: "xiaohong"}
createQuery(manager)
createQuery(xiaoHong)
}
使用示例:
反射修改值信息
package main
import (
"fmt"
"reflect"
)
//反射修改值
func reflect_set_value(a interface{}) {
t := reflect.TypeOf(a)
fmt.Println("type is ", t)
v := reflect.ValueOf(a)
k := v.Kind()
switch k {
case reflect.Float64:
// 反射修改值
v.SetFloat(6.9) // panic: reflect: reflect.Value.SetFloat using unaddressable value
fmt.Println("a is ", v.Float())
case reflect.Ptr:
// Elem()获取地址指向的值
v.Elem().SetFloat(7.9)
fmt.Println("case:", v.Elem().Float())
// 地址
fmt.Println(v.Pointer())
}
}
func main() {
var x float64 = 3.4
//reflect_set_value(x)
//fmt.Println("main:", x)
// 反射认为下面是指针类型,不是float类型
reflect_set_value(&x)
fmt.Println("main:", x)
}
首先,要修改值,必须传入指针类型
其次,传入指针,在反射时,得到的是reflect.Ptr而不是reflect.Float64;此时,需要用v.Elem()取到值然后再设置正确的值.
查看结构体的值,字段和方法
package main
import (
"fmt"
"reflect"
)
// 定义结构体
type User struct {
Id int
Name string
Age int
}
// 绑方法
func (u User) Hello() {
fmt.Println("Hello, I'm ", u.Name)
}
// 传入interface{}
func Poni(o interface{}) {
t := reflect.TypeOf(o)
fmt.Println("类型:", t)
fmt.Println("字段:", t.Name())
// 获取值
v := reflect.ValueOf(o)
fmt.Println(v)
// 可以获取所有属性
// 获取结构体字段个数:t.NumField()
for i := 0; i < t.NumField(); i++ {
// 取每个字段
f := t.Field(i)
fmt.Printf("%s : %v\t", f.Name, f.Type)
// 获取字段的值信息
// Interface():获取字段对应的值
//val := v.Field(i).Interface()
val := v.Field(i)
fmt.Println("val :", val)
}
fmt.Println("=================方法====================")
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Println(m.Name)
fmt.Println(m.Type)
}
}
func main() {
u := User{1, "xh", 20}
Poni(u)
}
注意value从v中获取,type和method从t中获取.因为type和method都存储在iTable中.
查看匿名字段
package main
import (
"fmt"
"reflect"
)
// 定义结构体
type User struct {
Id int
Name string
Age int
}
// 匿名字段
type Boy struct {
User
Addr string
}
func main() {
m := Boy{User{1, "zs", 20}, "bj"}
t := reflect.TypeOf(m)
fmt.Println(t)
// Anonymous:匿名
fmt.Printf("%#v\n", t.Field(0))
// 值信息
fmt.Printf("%#v\n", reflect.ValueOf(m).Field(0))
}
修改结构体的值
package main
import (
"fmt"
"reflect"
)
// 定义结构体
type User struct {
Id int
Name string
Age int
}
// 修改结构体值
func SetValue(o interface{}) {
v := reflect.ValueOf(o)
// 获取指针指向的元素
v = v.Elem()
// 取字段
f := v.FieldByName("Name")
if f.Kind() == reflect.String {
f.SetString("xiaoming")
}
}
func main() {
u := User{1, "xiaohong", 20}
SetValue(&u)
fmt.Println(u)
}
原理跟上述修改float类型的值类似,多了一个根据name取结构体field的功能.
调用方法
package main
import (
"fmt"
"reflect"
)
// 定义结构体
type User struct {
Id int
Name string
Age int
}
func (u User) Hello(name string) {
fmt.Println("Hello ", name, " My name is ", u.Name)
}
func main() {
u := User{1, "xiaohong", 20}
v := reflect.ValueOf(u)
// 获取方法
m := v.MethodByName("Hello")
// 构建一些参数
args := []reflect.Value{reflect.ValueOf("xiaoming")}
// 没参数的情况下:var args2 []reflect.Value *****调用无参数方法时,也需传入空参数
// 调用方法,需要传入方法的参数
m.Call(args)
}
*获取字段的tag*
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string `json:"tag1" db:"tag2"`
}
func main() {
var s Student
v := reflect.ValueOf(&s)
// 类型
t := v.Type()
// 获取字段
f := t.Elem().Field(0)
fmt.Println(f.Tag.Get("json"))
fmt.Println(f.Tag.Get("db"))
v = reflect.ValueOf(s)
t = v.Type()
f = t.Field(0)
fmt.Println(f.Tag.Get("json"))
fmt.Println(f.Tag.Get("db"))
}
参考: