Go 反射
文章转载地址:https://www.flysnow.org/2017/06/13/go-in-action-go-reflect.html
1. TypeOf 和 ValueOf
在 Go 的反射定义中,任何接口都由两部分组成,一个是接口的具体类型,一个是具体类型对应的值。比如:
var i int = 3,因为 interface{} 可以表示任何类型,所以变量 i 可以转换成 interface{} ,所以可以把变量 i 当成一个
接口,那么这个变量在 Go 反射中的表示就是 <type,value> ,其中 value 为变量的值 3,type 为类型 int
在 Go 反射中,标准库为我们提供两种类型来分别表示他们 reflect.Value 和 reflect.Type ,并提供两个函数来获
取任意对象的 Value 和 Type,看如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个 User 结构体
type User struct {
Name string
Age int
}
func main() {
u := User{"张三",25}
t := reflect.TypeOf(u)
fmt.Println(t)
}
-------------------------------
输出结果:
main.User
reflect.TypeOf 可以获取任意对象的具体类型,通过上面的例子可以看到打印输出的结果是 main.User 这个结构体类型
那么如何反射获取一个对象的 Value:
v := reflect.ValueOf(u)
fmt.Println(v)
--------------------------
输出结果:
{张三 25}
对于以上两种输出,Go 语言还通过 fmt.Printf 函数提供了更为简便的方法
fmt.Printf("%T\n",u)
fmt.Printf("%v\n",u)
2.reflect.Value 转原始类型
上面的例子中我们通过 reflect.ValueOf 函数把任意类型对象转换成 reflect.Value 类型,如果我们想逆向转换回来呢?其实也
是可以的,reflect.Value 为我们提供了 interface 方法,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个 User 结构体
type User struct {
Name string
Age int
}
func main() {
u := User{"张三",25}
// 使用 refelct.ValueOf 转换成 refelct.Value
v := reflect.ValueOf(u)
fmt.Println(reflect.TypeOf(v))
// 使用 interface 方法转换成 main.User
u1 := v.Interface().(User)
fmt.Println(reflect.TypeOf(u1))
}
-----------------------------------------------------
输出结果:
reflect.Value
main.User
3.获取类型底层类型
底层类型是什么意思?其实对应的主要是基础类型、接口、结构体、指针这些,因为我们可以通过 Type 关键字声明
很多新的类型,比如上面的例子,对象 u 的实际类型是 User,但对应的底层类型是 struct 这个结构体类型,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个 User 结构体
type User struct {
Name string
Age int
}
func main() {
u := User{"张三",25}
t := reflect.TypeOf(u)
fmt.Println(t.Kind())
}
--------------------------------
输出结果:
struct
4.遍历字段和方法
通过反射,我们可以获取一个结构体类型的字段,也可以获取一个类型的导出方法,这样我们就可以在运行时了解一个
类型的结构,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个 User 结构体
type User struct {
Name string
Age int
}
// 给结构体 User 绑定一个方法
func (u User) Print() {
fmt.Println("I am User")
}
func main() {
u := User{"张三",26}
t := reflect.TypeOf(u)
// 获取结构体类型的字段
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Name)
}
// 获取结构体类型的方法
for i := 0; i < t.NumMethod(); i++ {
fmt.Println(t.Method(i).Name)
}
}
---------------------------------------------------
输出结果:
Name
Age
Print
5.修改字段的值
我们还可以通过反射修改某个字段的值,如下示例(使用反射修改变量的值):
package main
import (
"fmt"
"reflect"
)
func main() {
x := 2
v := reflect.ValueOf(&x)
v.Elem().SetInt(100)
fmt.Println(x)
}
---------------------------------
输出结果:
100
因为 reflect.ValueOf 返回的是一份值的拷贝,所以要想修改一个变量的值前提是要传入修改变量的地址。
其次需要我们调用 Elem 方法找到指针指向的值。最后我们就可以使用 SetInt 方法修改值了
如何修改结构体字段的值呢?如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个结构体
type User struct {
Name string
Age int
}
func main() {
u := User{"tom",11}
v := reflect.ValueOf(&u.Name)
v.Elem().SetString("paul")
fmt.Println(u)
}
----------------------------------------
输出结果:
{paul 11}
6.动态调用方法
结构体的方法不仅可以正常调用,还可以通过反射调用。要想通过反射调用,我们先要获取到需要调用的方法,
然后进行传参调用,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个结构体
type User struct {
Name string
Age int
}
// 给 User 绑定一个方法
func (u User) Print(prfix string) {
fmt.Printf("%s:Name is %s,Age is %d",prfix,u.Name,u.Age)
}
func main() {
u := User{"lotus",26}
v := reflect.ValueOf(u)
// MethodByName 根据方法名获取方法对象
mPrint := v.MethodByName("Print")
// 构建参数
args := []reflect.Value{reflect.ValueOf("前缀")}
fmt.Println(mPrint.Call(args))
}
--------------------------------------------------------------
输出结果:
前缀:Name is lotus,Age is 26[]
MethodByName 可以根据一个方法名获取一个方法对象,然后我们构建好该方法需要的参数,最后调用
Call 就达到了动态调用方法的目的
7 通过反射获取 struct 的 Tag
字段的 Tag 是标记到字段上的,所以我们可以通过先获取字段,然后再获取标记在字段上的 Tag,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个 User 结构体
type User struct {
Name string `name`
Age int `age`
}
func main() {
var u User
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
fmt.Println(sf.Tag)
}
}
---------------------------------------------
输出结果:
name
age
通过 .Tag 就可以获取到对应的 Tag
8 字段 Tag 的键值对
一个 struct 的字段可能对应多个不同的 Tag,以便满足不同的功能场景,在 Go 中,struct 为我们提供了
键值对的 Tag ,来满足我们针对不同场景的需求,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个结构体
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var u User
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
fmt.Println(sf.Tag.Get("json"))
}
}
--------------------------------------------------
输出结果:
name
age
上面例子,使用了键值对的方式配置 struct Tag,Key-Value 以冒号分开,这里的 Key 就是 json,所以,我们
可以通过这个 Key 获取对应的值,也就是通过 .Tag.Get("json") 方法。 Get 方法就是通过一个 Key 获取对应的 tag 设置
除此之外,我们还可以通过设置多个 key,如下示例:
package main
import (
"fmt"
"reflect"
)
// 定义一个结构体
type User struct {
Name string `json:"name" bson:"b_name"`
Age int `json:"age" bson:"b_age"`
}
func main() {
var u User
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
fmt.Println(sf.Tag.Get("json"),",",sf.Tag.Get("bson"))
}
}
--------------------------------------------------------------------------
输出结果:
name , b_name
age , b_age
多个 Key 之间使用空格分开,然后通过 Get 方法获取不同的 Key 值


浙公网安备 33010602011771号