Go

反射的应用场景:结构体的tag(标签)底层用到的就是反射;编写函数的适配器 ,桥连接(自己设计go的框架);
 
基本介绍
    1.反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind);
    2.如果是结构体变量(实例),还可以获取到结构体本身的信息(包括结构体的字段,方法);
    3.通过反射,可以修改变量的值,可以调用关联的方法。
    4.使用反射,需要import( "reflect" )
 
反射重要的函数和概念
    1.reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type 类型
    2.reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型,reflect.Value 是一个结构体类型,通过reflect.Value,可以获取到关于该变量的很多信息。
    3.变量,interface{},和reflect.Value是可以相互转换的,这点在实际开发中,经常会用到。
        通常的操作方式:
            package main 
            import (     "reflect"     "fmt" ) 
            // 演示反射(基本类型) 
            func reflectTest01(t interface{}) {     
                    // 通过反射获取的传入的变量 type,kind, 值     
                    // 1.先获取到 reflect.Type      
                    rType := reflect.TypeOf(t)     
                    fmt.Println("rType==", rType)  // rType== int     
                    // 2.获取到 reflect.Value     
                    rVal := reflect.ValueOf(t)     
                    n2 := 2 + rVal.Int()     
                    fmt.Println("n2--", n2)                     
 
                    fmt.Printf("rVal==%v rVal type==%T  ", rVal, rVal)  // rVal==100 rVal type==reflect.Value     
 
                    // 3.将rVal 转成 interface{}     这里转成interface就是原来的类型了,为啥需要用断言呢?因为编译器无法通过
                    iV := rVal.Interface()     
                    // 4.将 iV 通过断言转成原来或者需要的类型     
                    num2 := iV.(int)     
                    fmt.Printf("num2===%v num2 type ==%T  ", num2, num2) // num2===100 num2 type ==int 
             } 
             func main() {     
                    // 演示对(基本数据类型,interface{}, reflect.Value)进行反射的基本操作     
                    var num int = 100     
                    reflectTest01(num) 
            }
 
反射注意事项和细节说明
    1.reflect.Value.Kind 获取变量的类别(类别范围大于类型,电器(冰箱,洗衣机等),电器就是类别,里面包含的就是类型),返回的是一个常量
    2.Type是类型,Kind是类别,Type 和 Kind 可能是相同的,也可能是不同的
        比如:var num int = 20 num的Type是int,Kind也是int(基本数据类型两者是一样的);
                    var stu  Student  stu的Type是 包名.Student,Kind是struct;
    3.通过反射可以让变量interface{}reflect.Value 之间相互转换;
    4.使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不是使用其他的,否则报panic;
    5.通过反射来修改变量,注意当使用SetXx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem() 方法;
    实际案例:
        var num1 int = 20    
        reflectTest01(&num1)     
        fmt.Println("num1---", num1)   // num1--- 30
 
        reflectTest01里的代码:
            // 获取到 reflect.Value    
            rVal := reflect.ValueOf(t)      
            // Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装     
            rVal.Elem().SetInt(30)    // 即完成修改
    
        // 更好的理解 rVal.Elem()    
        num3 := 40     
        ptr *int = &num3      
        num4 := *ptr   // 等价于 rVal.Elem()
 
 
反射的最佳实践
    1.使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
        实际案例:
            package main 
            import (     "fmt"     "reflect" ) 
            // 定义一个结构体 
            type Monster struct {     
                    Name string  `json:"name"`     
                    Age int   `json:"monster_age"`     
                    Score float64     
                    Sex  string  
            } 
            // 方法,显示Monster实例的值 
            func (m Monster) Print() {     
                    fmt.Println("----start----")     
                    fmt.Println(m)     
                    fmt.Println("----end----") 
            } 
            // 方法,返回两个数的和 
            func (m Monster) GetSum(n1, n2 int) int {     
                    return n1 + n2  
            } 
            // 方法,接收四个值,给Monster实例赋值 
            func (m Monster) SetVal(name string, age int, score float64, sex string) {     
                    m.Name = name      
                    m.Age = age      
                    m.Score = score     
                    m.Sex = sex  
            } 
            // 方法 结构体测试 
            func TestStruct(t interface{}) {     
                    // 获取reflect.Type 类型     
                    rType := reflect.TypeOf(t)     
 
                    // 获取reflect.Value 类型     
                    rVal := reflect.ValueOf(t)     
 
                    // 获取到t对应的类别     
                    kd := rVal.Kind()     
                    // 如果传入的不是struct,就退出     
                    if kd != reflect.Ptr && rVal.Elem().Kind() != reflect.Struct {         
                            fmt.Println("expect struct ptr")         
                            return      
                    }     
                    // 获取到t结构体有几个字段     
                    f_num := rVal.Elem().NumField()     
                    fmt.Printf("struct has %d fields  ", f_num)     
 
                    // 遍历结构体的所有字段     
                    for i:=0; i < f_num; i ++ {         
                            // 获取struct每个字段的值,但是此时获取的不能做任何操作,如果想操作可以转         
                            fmt.Printf("Field %d : 值为=%v  ", i, rVal.Elem().Field(i))         
                            // 获取到struct标签,注意需要通过reflect.Type 来获取tag标签的值         
 
                            tagVal := rType.Elem().Field(i).Tag.Get("json")         
                            if tagVal != "" { // 有些字段没有标签             
                                    fmt.Printf("Field %d: tag为=%v  ", i, tagVal)         
                            }     
                    }     
                    // 获取到结构体有多少个方法     
                    numOfMethod := rVal.Elem().NumMethod()     
                    fmt.Printf("struct has %d methods  ", numOfMethod)     
 
                    // var params []reflect.Value     
                    rVal.Elem().Method(1).Call(nil)  // 获取到第二个方法且调用(但排序实际是按函数名字母的ASCII码)   
  
                    // 调用结构体的第一个方法Method(0)     
                    var params []reflect.Value  // 定义了 []reflect.Value     
                    params = append(params, reflect.ValueOf(10))     
                    params = append(params, reflect.ValueOf(60))     
                    res_slice := rVal.Elem().Method(0).Call(params)  // 传入的参数是 []reflect.Value, 返回的结果类型也是[]reflect.Value     
                    fmt.Printf("res_slice=%v type ==%T  ", res_slice[0].Int(), res_slice[0].Int())  // 返回结果是 []reflect.Value  
 
                    // 修改结构体字段值 需要传 地址    
                    rVal.Elem().Field(0).SetString("沙和尚")     
                    rVal.Elem().Method(1).Call(nil) 
            } 
             func main() {     
                    // 使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值     
                    m := Monster{         
                            Name : "孙悟空",         
                            Age : 500,         
                            Score : 99.9,         
                            Sex : "男",     
                    }     
                    // TestStruct(m)     // 只是读 
                    TestStruct(&m)     // 传地址可以修改字段值
                    fmt.Println(m) 
            }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/guo-s/p/14250705.html