前言
golang 反射
指的是程序在运行中可以动态获取变量的类型,方法,属性,并且可以调用方法以及修改属性。 golang 反射核心是通过 reflect.TypeOf() 和 reflect.ValueOf() 展开,下边我开始为大家展开golang 反射的谜团。
正文
1 获取变量类型
1.1 通过 reflect.TypeOf() 获取
package main
import (
"fmt"
"reflect"
)
func main() {
i := 1
str := "hello world"
obj := struct {
}{}
m := map[string]interface{}{}
var sl []interface{}
fmt.Println(reflect.TypeOf(i))
fmt.Println(reflect.TypeOf(str))
fmt.Println(reflect.TypeOf(obj))
fmt.Println(reflect.TypeOf(m))
fmt.Println(reflect.TypeOf(sl))
}
打印结果
int
string
struct {}
map[string]interface {}
[]interface {}
1.2 通过 reflect.ValueOf() 获取
package main
import (
"fmt"
"reflect"
)
func main() {
i := 1
str := "hello world"
obj := struct {
}{}
m := map[string]interface{}{}
var sl []interface{}
fmt.Println(reflect.ValueOf(i).Type())
fmt.Println(reflect.ValueOf(str).Type())
fmt.Println(reflect.ValueOf(obj).Type())
fmt.Println(reflect.ValueOf(m).Type())
fmt.Println(reflect.ValueOf(sl).Type())
}
打印结果 和 reflect.TypeOf() 是一样的
int
string
struct {}
map[string]interface {}
[]interface {}
1.3 type 和 kind
对于基本类型 type 和 kind 是一样的,对于自定义 类型,kind 和 type 就不一样的。
package main
import (
"fmt"
"reflect"
)
type integer int
func main() {
var a integer
var b int64
fmt.Println(reflect.TypeOf(a), reflect.TypeOf(a).Kind())
fmt.Println(reflect.TypeOf(b), reflect.TypeOf(b).Kind())
}
打印结果
main.integer int
int64 int64
说明type是变量的上层类型,对自定义类型integer ,他的type是main.integer,但是底层类型是int,然而基本类型kind 和 type 是一样的。
2 获取自定变量属性
2.1 获取struct类型其属性值
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
sex string
}
func main() {
person := people{18, "女"}
c := reflect.ValueOf(person)
fmt.Println(c.NumField()) //获取属性数量
fmt.Println(c.Field(0)) //获取一个属性值
fmt.Println(c.FieldByIndex([]int{1})) //获取第二个属性值
fmt.Println(c.FieldByName("sex")) //获取属性名称为sex的属性值
}
2.2 获取struct类型属性名称和类型
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
sex string
}
func main() {
person := people{18, "女"}
c := reflect.ValueOf(person)
fmt.Println(c.Type().Field(0).Type) //获取第一个属性类型
fmt.Println(c.Type().Field(0).Name) //获取第一个属性名称
fmt.Println(c.Type().Field(1).Type) //获取第二个属性类型
fmt.Println(c.Type().Field(1).Name) //获取第二个属性名称
}
打印结果
int64
age
string
sex
3 修改变量属性值
3.1 修改struct变量属性值
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
Sex string
}
func main() {
person := people{18, "女"}
c := reflect.ValueOf(&person).Elem() //传递变量地址,且获取变量值
if c.FieldByName("Sex").CanSet() { //判断变量是否可写
c.FieldByName("Sex").SetString("男") //修改sex值
}
fmt.Println(c)
}
打印结果
{18 男}
如果要struct 变量值,那么 valueOf() 传递的是变量的地址,然后通过Elem获取变量对于的值,然后才能修改,如果仅仅传递量值,那么变量是不能被修改的
3.2 修改其他类型变量
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
Sex string
}
func main() {
a := 1 //创建变量1
reflect.ValueOf(&a).Elem().SetInt(2)
fmt.Println(a)
}
打印结果
2
4 调用变量的方法
4.1 获取struct变量的方法
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
Sex string
}
func (p *people) Say(str string) string {
fmt.Println(str)
return str
}
func main() {
p := people{18, "男"}
v := reflect.ValueOf(&p)
fmt.Println(v.NumMethod()) //获取方法数量
method := v.MethodByName("Say")
fmt.Println(method.Type().NumIn()) //获取参数个数
}
打印结果
1
1
4.2 通过valueOf 调用 stuct的方法
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
Sex string
}
func (p *people) Say(str string) string {
fmt.Println(str)
return str
}
func main() {
p := people{18, "男"} //初始化变量
v := reflect.ValueOf(&p) //获取变量的值
method := v.MethodByName("Say") //获取变量的方法
args := make([]reflect.Value,method.Type().NumIn()) //构成方法参数
args[0] = reflect.ValueOf("hello world") //第一个参数赋值
result := method.Call(args) //调用方法
fmt.Println(result[0])//打印结果
}
输出
hello world
hello world
4.3 通过typeOf 调用 stuct的方法
package main
import (
"fmt"
"reflect"
)
type people struct {
age int64
Sex string
}
func (p *people) Say(str string) string {
fmt.Println(str)
return str
}
func main() {
p := people{18, "男"} //初始化变量
t := reflect.TypeOf(&p) //获取变量的类型
method, _ := t.MethodByName("Say") // 获取变量的方法
args := make([]reflect.Value,method.Type.NumIn()) // 构成参数,于valueof 不同,typeof 需要传对象本身进去
args[0] = reflect.ValueOf(&p) // 第一个参数为变量本身
args[1] = reflect.ValueOf("hello world") //第二个才为参数
result := method.Func.Call(args) //调用方法
fmt.Println(result[0])//打印结果
}
打印结果
hello world
hello world
总结
golang 反射可以支持在程序中根据变量获取边的属性方法,已经修改属性,调用变量的方法,可以大幅度降低我们的代码量。