zoukankan      html  css  js  c++  java
  • 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) 
                }
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    PHP:第一章——PHP中的goto语句和
    PHP:第二章——PHP中的foreach语句
    Swingr的JTextField、JPasswordField设置圆角输入框
    Jtabbedpane设置透明、Jpanel设置透明
    去掉utf-8的Bom头:使用java以及jdbc不使用第三方库执行sql文件脚本
    使用java以及jdbc不使用第三方库执行sql文件脚本
    JButton ButtonClickTest
    Md5加密
    JButton变换样式
    grub2详解(翻译和整理官方手册)
  • 原文地址:https://www.cnblogs.com/guo-s/p/14250705.html
Copyright © 2011-2022 走看看