go-反射
- 作者: 毋需多言
- 来源: 51数据库
- 2021-08-11
反射
反射的基本介绍
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
}
