用户登录
用户注册

分享至

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
}


软件
前端设计
程序设计
Java相关