Go语言反射
隐姓埋名4869 人气:0反射指的是运行时动态的获取变量的相关信息
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() { //实例化 stu := &Student{"张三", 19} //反射获取值:指针方式 stuV := reflect.ValueOf(&stu).Elem() fmt.Println("修改前: ", stuV.MethodByName("String").Call(nil)[0]) //修改值 params := make([]reflect.Value, 1) //定义切片 params[0] = reflect.ValueOf("李四") stuV.MethodByName("SetName").Call(params) params[0] = reflect.ValueOf(20) stuV.MethodByName("SetAge").Call(params) fmt.Println("修改后: ", stuV.MethodByName("String").Call(nil)[0]) }
输出结果如下
修改前: 0xc000004078,Name:张三,Age:19
修改后: 0xc000004078,Name:李四,Age:20
6.2 使用 method 索引调用方法
package main import ( "fmt" "reflect" "strconv" ) //反射方法 type Student struct { Name string Age int } //结构体方法 func (s *Student) B(name string) { s.Name = name } func (s *Student) A(age int) { s.Age = age } func (s *Student) C() string { return fmt.Sprintf("%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age) } func main() { //实例化 stu := &Student{"张三", 19} //反射获取值:指针方式 stuV := reflect.ValueOf(&stu).Elem() //索引调用方法 fmt.Println("修改前: ", stuV.Method(2).Call(nil)[0]) params := make([]reflect.Value, 1) params[0] = reflect.ValueOf("李四") stuV.Method(1).Call(params) params[0] = reflect.ValueOf(20) stuV.Method(0).Call(params) fmt.Println("修改后: ", stuV.Method(2).Call(nil)[0]) //调用索引大小取决于方法名称的ASCII大小进行排序 }
输出结果如下
修改前: 0xc000004078,Name:张三,Age:19
修改后: 0xc000004078,Name:李四,Age:20
加载全部内容