go-反射
反射
反射的基本介绍
17.3.1 基本介绍
1) 反射可以在运行时 动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
2) 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的 字段、 方法)
3) 通过反射,可以修改变量的值,可以调用关联的方法。
4) 使用反射,需要 import (“reflect”)
反射重要的函数和概念
1) reflect.typeof(变量名),获取变量的类型,返回reflect.type
2) reflect.value(变量名),获取变量的值,返回reflect.value(是一个结构体类型)
3) 变量、interface{} 和 reflect.value 是可以相互转换的,这点在实际开发中,会经常使用到
反射的快速入门
快速入门说明
请编写一个案例,演示对(基本数据类型、interface{}、reflect.value)进行反射的基本操作
代码演示,见下面的表格:
请编写一个案例,演示对(结构体类型、interface{}、reflect.value)进行反射的基本操作
package main import ( "reflect" "fmt" ) //专门演示反射 func reflecttest01(b interface{}) { //通过反射获取的传入的变量的 type , kind, 值 //1. 先获取到 reflect.type rtyp := reflect.typeof(b) fmt.println("rtype=", rtyp) //2. 获取到 reflect.value rval := reflect.valueof(b) n2 := 2 + rval.int() //n3 := rval.float() fmt.println("n2=", n2) //fmt.println("n3=", n3) fmt.printf("rval=%v rval type=%t\n", rval, rval) fmt.printf("rval=%v rtyp type=%t\n", rval, rtyp) //下面我们将 rval 转成 interface{} iv := rval.interface() //将 interface{} 通过断言转成需要的类型 num2 := iv.(int) fmt.println("num2=", num2) } //专门演示反射[对结构体的反射] func reflecttest02(b interface{}) { //通过反射获取的传入的变量的 type , kind, 值 //1. 先获取到 reflect.type rtyp := reflect.typeof(b) fmt.println("rtype=", rtyp) //2. 获取到 reflect.value rval := reflect.valueof(b) //3. 获取 变量对应的kind //(1) rval.kind() ==> kind1 := rval.kind() //(2) rtyp.kind() ==> kind2 := rtyp.kind() fmt.printf("kind =%v kind=%v\n", kind1, kind2) //下面我们将 rval 转成 interface{} iv := rval.interface() fmt.printf("iv=%v iv type=%t \n", iv, iv) //将 interface{} 通过断言转成需要的类型 //这里,我们就简单使用了一带检测的类型断言. //同学们可以使用 swtich 的断言形式来做的更加的灵活 stu, ok := iv.(student) if ok { fmt.printf("stu.name=%v\n", stu.name) } } type student struct { name string age int } type monster struct { name string age int } func main() { //请编写一个案例, //演示对(基本数据类型、interface{}、reflect.value)进行反射的基本操作 //1. 先定义一个int var num int = 100 reflecttest01(num) //2. 定义一个student的实例 stu := student{ name : "tom", age : 20, } reflecttest02(stu) }
反射的注意事项和细节
1) reflect.value.kind,获取变量的类别,返回的是一个常量
2) type 和 kind 的区别:
type 是类型, kind 是类别, type 和 kind 可能是相同的,也 可能是不同的. 比如: var num int = 10 num 的 type 是 int , kind 也是 int 比如: var stu student stu 的 type 是 pkg1.student , kind 是 struct
3) 通过反射可以让变量在interface{}和reflect.value之间互相转换。
4) 通过反射获取的变量的值,要求数据类型配,比如x是int,那么就应该用reflect.value.int(),而不能用其他,否则会报panic的错误。
5) 通过反射的来修改变量, 注意当使用 setxxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.value.elem()方法
package main import ( "reflect" "fmt" ) //通过反射,修改, // num int 的值 // 修改 student的值 func reflect01(b interface{}) { //2. 获取到 reflect.value rval := reflect.valueof(b) // 看看 rval的kind是 fmt.printf("rval kind=%v\n", rval.kind()) //3. rval //elem返回v持有的接口保管的值的value封装,或者v持有的指针指向的值的value封装 rval.elem().setint(20) } func main() { var num int = 10 reflect01(&num) fmt.println("num=", num) // 20 //你可以这样理解rval.elem() // num := 9 // ptr *int = &num // num2 := *ptr //=== 类似 rval.elem() }
package main import ( "fmt" "reflect" ) func main() { var str string = "tom" //ok fs := reflect.valueof(&str) //ok fs -> string fs.elem().setstring("jack") //ok fmt.printf("%v\n", str) // jack }
反射最佳实践
1) 使用 反射来遍历结构体的字段, 调用结构体的方法,并 获取结构体标签的值
2) 使用反射的方式来获取结构体的 tag 标签, 遍历字段的值,修改字段值,调用结构体方法(要求:通过传递地址的方式完成, 在前面案例上修改即可)
3) 定义了两个函数 test1 和 test2,定义一个适配器函数用作统一处理接口(略,用反射实现即可)
4) 使用反射操作任意结构体类型:
5) 使用反射创建并操作结构体
package main import ( "fmt" "reflect" ) //定义了一个monster结构体 type monster struct { name string `json:"name"` age int `json:"monster_age"` score float32 `json:"成绩"` sex string } //方法,返回两个数的和 func (s monster) getsum(n1, n2 int) int { return n1 + n2 } //方法, 接收四个值,给s赋值 func (s monster) set(name string, age int, score float32, sex string) { s.name = name s.age = age s.score = score s.sex = sex } //方法,显示s的值 func (s monster) print() { fmt.println("---start~----") fmt.println(s) fmt.println("---end~----") } func teststruct(a interface{}) { //获取reflect.type 类型 typ := reflect.typeof(a) //获取reflect.value 类型 val := reflect.valueof(a) //获取到a对应的类别 kd := val.kind() //如果传入的不是struct,就退出 if kd != reflect.struct { fmt.println("expect struct") return } //获取到该结构体有几个字段 num := val.numfield() fmt.printf("struct has %d fields\n", num) //4 //变量结构体的所有字段 for i := 0; i < num; i++ { fmt.printf("field %d: 值为=%v\n", i, val.field(i)) //获取到struct标签, 注意需要通过reflect.type来获取tag标签的值 tagval := typ.field(i).tag.get("json") //如果该字段于tag标签就显示,否则就不显示 if tagval != "" { fmt.printf("field %d: tag为=%v\n", i, tagval) } } //获取到该结构体有多少个方法 numofmethod := val.nummethod() fmt.printf("struct has %d methods\n", numofmethod) //var params []reflect.value //方法的排序默认是按照 函数名的排序(ascii码) val.method(1).call(nil) //获取到第二个方法。调用它 //调用结构体的第1个方法method(0) var params []reflect.value //声明了 []reflect.value params = append(params, reflect.valueof(10)) params = append(params, reflect.valueof(40)) res := val.method(0).call(params) //传入的参数是 []reflect.value, 返回[]reflect.value fmt.println("res=", res[0].int()) //返回结果, 返回的结果是 []reflect.value*/ } func main() { //创建了一个monster实例 var a monster = monster{ name: "黄鼠狼精", age: 400, score: 30.8, } //将monster实例传递给teststruct函数 teststruct(a) }
const介绍
package main import ( "fmt" ) func main() { var num int num = 9 //ok //常量声明的时候,必须赋值。 const tax int = 0 //常量是不能修改 //tax = 10 fmt.println(num, tax) //常量只能修饰bool、数值类型(int, float系列)、string 类型 //fmt.println(b) const ( a = iota b c d ) fmt.println(a, b, c, d)//0 1 2 3 }