欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

go-反射

程序员文章站 2023-12-27 20:47:27
反射 反射的基本介绍 17.3.1 基本介绍 1) 反射可以在运行时 动态获取变量的各种信息, 比如变量的类型(type),类别(kind) 2) 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的 字段、 方法) 3) 通过反射,可以修改变量的值,可以调用关联的方法。 4) 使用反射,需 ......

反射

反射的基本介绍

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
}


上一篇:

下一篇: