您现在的位置是:网站首页> 编程资料编程资料

Go语言学习之反射的用法详解_Golang_

2023-05-26 388人已围观

简介 Go语言学习之反射的用法详解_Golang_

反射指的是运行时动态的获取变量的相关信息

1. reflect 包

类型是变量,类别是常量

reflect.TypeOf,获取变量的类型,返回reflect.Type类型

reflect.ValueOf,获取变量的值,返回reflect.Value类型

reflect.Value.Kind,获取变量的类别,返回一个常量

reflect.Value.Interface(),转换成interface{}类型

1.1 获取变量类型

package main import ( "fmt" "reflect" ) func Test(i interface{}) { //反射数据类型 t := reflect.TypeOf(i) fmt.Println("类型是", t) //反射数据值 v := reflect.ValueOf(i) fmt.Println("值是", v) } func main() { a := "hello" Test(a) }

输出结果如下

类型是 string
值是 hello

package main import ( "fmt" "reflect" ) type Student struct { Name string Age int Score float32 } func Test(i interface{}) { //反射获取类型 t := reflect.TypeOf(i) fmt.Println("类型是", t) //反射获取值 v := reflect.ValueOf(i) //判断值的类别 c := v.Kind() fmt.Println("类别是", c) } func main() { var stu Student = Student{ Name: "张三", Age: 18, Score: 80, } Test(stu) fmt.Println("-------------") var num int = 10 Test(num) }

输出结果如下

类型是 main.Student
类别是 struct
-------------
类型是 int
类别是 int

1.2 断言处理类型转换

package main import ( "fmt" "reflect" ) type Student struct { Name string Age int Score float32 } func Test(i interface{}) { t := reflect.TypeOf(i) fmt.Println("类型是", t) //类别 v := reflect.ValueOf(i) c := v.Kind() fmt.Println("类别是", c) fmt.Printf("c的类型是%T\n", c) fmt.Printf("v的类型是%T\n", v) //转换成接口 iv := v.Interface() fmt.Printf("iv的类型%T\n", iv) //断言处理 stu_iv, err := iv.(Student) if err { fmt.Printf("stu_iv的类型%T\n", stu_iv) } } func main() { var stu Student = Student{ Name: "张三", Age: 18, Score: 80, } Test(stu) }

输出结果如下

类型是 main.Student
类别是 struct
c的类型是reflect.Kind
v的类型是reflect.Value
iv的类型main.Student
stu_iv的类型main.Student

2. ValueOf

2.1 获取变量值

reflect.valueof(x).Float()

reflect.valueof(x).Int()

reflect.valueof(x).String()

reflect.Valueof(x).Bool()

2.2 类型转换

package main import ( "fmt" "reflect" ) func Test(i interface{}) { v := reflect.ValueOf(i) fmt.Printf("v的类型是%T\n", v) //转换成指定类型 t := v.Int() fmt.Printf("t的类型是%T\n", t) } func main() { //类型不同的话会报错 var num int = 100 Test(num) }

输出结果如下

v的类型是reflect.Value
t的类型是int64

3. Value.Set

3.1 设置变量值

reflect.Value.SetFloat(),设置浮点数

reflect.value.SetInt(),设置整数

reflect.Value.SetString(),设置字符串

3.2 示例

package main import ( "fmt" "reflect" ) func Test(i interface{}) { v := reflect.ValueOf(i) //更新值需要value的地址,否则会保存,Elem()表示指针* v.Elem().SetInt(100) result := v.Elem().Int() fmt.Printf("result类型为 %T, 值为 %d\n", result, result) } func main() { var num int = 10 Test(&num) }

输出结果如下

result类型为 int64, 值为 100

4. 结构体反射

反射出结构体的属性和方法数量

方法名需大写,需要被跨包调用识别

4.1 查看结构体字段数量和方法数量

package main import ( "fmt" "reflect" ) //结构体 type Student struct { Name string Age int Score float32 } //结构体方法 func (s Student) Run() { fmt.Println("Running") } func (s Student) Sleep() { fmt.Println("Sleeping") } //使用反射查看结构体字段数量和方法数量 func Test(i interface{}) { v := reflect.ValueOf(i) //类别判断 if v.Kind() != reflect.Struct { fmt.Println("不是结构体") return } //获取结构体中字段数量 stu_num := v.NumField() fmt.Println("字段数量: ", stu_num) //获取结构体中方法数量 stu_meth := v.NumMethod() fmt.Println("方法数量: ", stu_meth) } func main() { var stu Student = Student{ Name: "张三", Age: 18, Score: 88, } Test(stu) }

输出结果如下

字段数量:  3
方法数量:  2

4.2 获取结构体属性

package main import ( "fmt" "reflect" ) type Student struct { Name string Age int Score float32 } func Test(i interface{}) { v := reflect.ValueOf(i) //获取结构体中每个属性 for i := 0; i < v.NumField(); i++ { //输出属性值 fmt.Printf("%d %v\n", i, v.Field(i)) //输出属性值的类型 fmt.Printf("%d %v\n", i, v.Field(i).Kind()) } } func main() { var stu Student = Student{ Name: "张三", Age: 18, Score: 88, } Test(stu) }

输出结果如下

0 张三
0 string
1 18
1 int
2 88
2 float32

4.3 更改属性值

package main import ( "fmt" "reflect" ) type Student struct { Name string Age int Score float32 } func Test(i interface{}, name string) { v := reflect.ValueOf(i) vk := v.Kind() //判断是都为指针并指向结构体类型 if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct { fmt.Println("expect struct") return } //更改属性值 v.Elem().Field(0).SetString(name) //获取结构体中每个属性 for i := 0; i < v.Elem().NumField(); i++ { //输出属性值 fmt.Printf("%d %v\n", i, v.Elem().Field(i)) //输出属性值的类型 fmt.Printf("%d %v\n", i, v.Elem().Field(i).Kind()) } } func main() { var stu Student = Student{ Name: "张三", Age: 18, Score: 88, } Test(&stu, "李四") }

输出结果如下

0 李四
0 string
1 18
1 int
2 88
2 float32

4.4 Tag原信息处理

package main import ( "encoding/json" "fmt" "reflect" ) type Student struct { Name string `json:"stu_name"` Age int Score float32 } func Test(i interface{}, name string) { v := reflect.ValueOf(i) vk := v.Kind() //判断是都为指针并指向结构体类型 if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct { fmt.Println("expect struct") return } //更改属性值 v.Elem().Field(0).SetString(name) //获取结构体中每个属性 for i := 0; i < v.Elem().NumField(); i++ { //输出属性值 fmt.Printf("%d %v\n", i, v.Elem().Field(i)) //输出属性值的类型 fmt.Printf("%d %v\n", i, v.Elem().Field(i).Kind()) } } func main() { var stu Student = Student{ Name: "张三", Age: 18, Score: 88, } Test(&stu, "李四") fmt.Println("----------------json原信息----------------") result, _ := json.Marshal(stu) fmt.Println("json原信息: ", string(result)) //反射获取类型 st := reflect.TypeOf(stu) s := st.Field(0) fmt.Printf("Name原信息名称: %s\n", s.Tag.Get("json")) } 

输出结果如下

0 李四
0 string
1 18
1 int
2 88
2 float32
----------------json原信息----------------
json原信息:  {"stu_name":"李四","Age":18,"Score":88}
Name原信息名称: stu_name

5. 函数反射

Go 中函数是可以赋值给变量的

示例:

既然函数可以像普通的类型变量一样,那么在反射机制中就和不同的变量是一样的,在反射中函数和方法的类型(Type)都是reflect.Func,如果要调用函数,通过 Value 的Call() 方法

package main import ( "fmt" "reflect" ) func hello() { fmt.Println("hello world") } func main() { //反射使用函数 v := reflect.ValueOf(hello) //类型判断是否属于reflect.func类型 if v.Kind() == reflect.Func { fmt.Println("函数") } //反射调用函数 v.Call(nil) //Call中需要传入的是切片 }

输出结果如下

函数
hello world

package main import ( "fmt" "reflect" "strconv" ) //反射调用传参和返回值函数 func Test(i int) string { return strconv.Itoa(i) } func main() { v := reflect.ValueOf(Test) //定义参数切片 params := make([]reflect.Value, 1) //切片元素赋值 params[0] = reflect.ValueOf(20) //反射调函数 result := v.Call(params) fmt.Printf("result的类型是 %T\n", result) //[]reflect.Value切片转换string s := result[0].Interface().(string) fmt.Printf("s的类型是 %T ,值为 %s\n", s, s) } 

输出结果如下

result的类型是 []reflect.Value
s的类型是 string ,值为 20

6. 方法反射

反射中方法的调用,函数和方法可以说其实本质上是相同的,只不过方法与一个“对象”进行了“绑定”,方法是“对象”的一种行为,这种行为是对于这个“对象”的一系列操作,例如修改“对象”的某个属性

6.1 使用 MethodByName 名称调用方法

package main import ( "fmt" "reflect" "strconv" ) //反射方法 type Student struct { Name string Age int } //结构体方法 func (s *Student) SetName(name string) { s.Name = name } func (s *Student) SetAge(age int) { s.Age = age } func (s *Student) String() string { return fmt.Sprintf("%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age) } func main() 
                
                

-六神源码网