go 反射

作者: 乘风破浪_6a9f | 来源:发表于2019-05-19 15:28 被阅读0次

    反射的概念

    反射就是程序能够在运行时动态的查看自己的状态,比关切允许修改自
    身的行为。
    1、GO的反射基础是接口和类型系统,
    2、反射的优点:1)、通用性,可对类库和框架代码做极大的简化设计;2)、灵活性,对一些测试工具的开发提供良好的支持。
    3、反射的缺点:1)、脆弱,不正确的修改很容易导致程序的崩溃;2)、难懂,反射接口运行时,没有具体的类型系统的约束,接口的抽象及实现细节复杂,导致代码难以理解;3)、性能损失,动态修改状态必然不是直接的地址引用,而是借助运行时构造一个抽象层来间接访问,导致性能损失。
    4、用途:尽量在一些库或框架内部代码中使用。
    5、Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,Golang的gRPC也是通过反射实现的。

    所有类型通用的方法
    
    //返回包含包名的类型名字,对于未命名类型返回的是空
    Name() string
    
    //Kind返回该类型的底层基础类型
    Kind() Kind
    
    
    //确定当前类型是否实现了u接口类型
    //注意这里的u必须是接口类型的Type
    Implements(u Type) bool
    
    //判断当前类型的实例是否能赋值给type为u的类型变量
    AssignableTo(u Type) bool
    
    //判断当前类型的实例是否能强制类型转换为u类型变量
    ConvertibleTo(u Type) bool
    
    //判断当前类型是否支持比较(等于或不等于)
    //支持等于的类型可以作为map的key
    Comparable() bool
    
    //返回一个类型的方法的个数
    NumMethod() int
    
    //通过索引值访问方法,索引值必须属于[0,NumMethod()],否则引发panic
    Method(int) Method
    
    //通过方法名获取Method
    MethodByName(string) (Method,bool)
    
    //返回类型的包路径,如果类型是预声明类型或未命名类型,则返回空字符串
    PkgPath() string
    
    //返回存放该类型的实例需要多大的字节空间
    size() uintptr
    
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type User struct {
        Id   int
        Name string
        Age  int
    }
    
    func (u User) ReflectCallFunc() {
        fmt.Println("Allen.Wu ReflectCallFunc")
    }
    
    func main() {
    
        user := User{1, "Allen.Wu", 25}
    
        DoFiledAndMethod(user)
    
    }
    
    // 通过接口来获取任意参数,然后一一揭晓
    func DoFiledAndMethod(input interface{}) {
    
        getType := reflect.TypeOf(input)
        fmt.Println("get Type is :", getType.Name())
    
        getValue := reflect.ValueOf(input)
        fmt.Println("get all Fields is:", getValue)
    
        // 获取方法字段
        // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
        // 2. 再通过reflect.Type的Field获取其Field
        // 3. 最后通过Field的Interface()得到对应的value
        for i := 0; i < getType.NumField(); i++ {
            field := getType.Field(i)
            value := getValue.Field(i).Interface()
            fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
        }
    
        // 获取方法
        // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
        for i := 0; i < getType.NumMethod(); i++ {
            m := getType.Method(i)
            fmt.Printf("%s: %v\n", m.Name, m.Type)
        }
    }
    
    
    运行结果:
    get Type is : User
    get all Fields is: {1 Allen.Wu 25}
    Id: int = 1
    Name: string = Allen.Wu
    Age: int = 25
    ReflectCallFunc: func(main.User)
    
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type User struct {
        Id   int
        Name string
        Age  int
    }
    
    func (u User) ReflectCallFuncHasArgs(name string, age int) {
        fmt.Println("ReflectCallFuncHasArgs name: ", name, ", age:", age, "and origal User.Name:", u.Name)
    }
    
    func (u User) ReflectCallFuncNoArgs() {
        fmt.Println("ReflectCallFuncNoArgs")
    }
    
    // 如何通过反射来进行方法的调用?
    // 本来可以用u.ReflectCallFuncXXX直接调用的,但是如果要通过反射,那么首先要将方法注册,也就是MethodByName,然后通过反射调动mv.Call
    
    func main() {
        user := User{1, "Allen.Wu", 25}
        
        // 1. 要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value,得到“反射类型对象”后才能做下一步处理
        getValue := reflect.ValueOf(user)
    
        // 一定要指定参数为正确的方法名
        // 2. 先看看带有参数的调用方法
        methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
        args := []reflect.Value{reflect.ValueOf("wudebao"), reflect.ValueOf(30)}
        methodValue.Call(args)
    
        // 一定要指定参数为正确的方法名
        // 3. 再看看无参数的调用方法
        methodValue = getValue.MethodByName("ReflectCallFuncNoArgs")
        args = make([]reflect.Value, 0)
        methodValue.Call(args)
    }
    
    
    运行结果:
    ReflectCallFuncHasArgs name:  wudebao , age: 30 and origal User.Name: Allen.Wu
    ReflectCallFuncNoArgs
    

    说明
    1、要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value,得到“反射类型对象”后才能做下一步处理

    2、reflect.Value.MethodByName这.MethodByName,需要指定准确真实的方法名字,如果错误将直接panic,MethodByName返回一个函数值对应的reflect.Value方法的名字。

    3、[]reflect.Value,这个是最终需要调用的方法的参数,可以没有或者一个或者多个,根据实际参数来定。

    4、reflect.Value的 Call 这个方法,这个方法将最终调用真实的方法,参数务必保持一致,如果reflect.Value'Kind不是一个方法,那么将直接panic。

    5、本来可以用u.ReflectCallFuncXXX直接调用的,但是如果要通过反射,那么首先要将方法注册,也就是MethodByName,然后通过反射调用methodValue.Call

    使用反射的实例:inject包 依赖注入:https://www.zddhub.com/memo/2015/07/05/go-dependency-inject.html

    反射三定律:
    (1)反射可以从接口值得到反射对象;
    (2)反射可以从反射对象获取接口值;
    (3)若要修改一个反射对象,则其值必须为可修改。

    1、Golang的gRPC也是通过反射实现的。
    2、控制反转,依赖注入

    类型断言在GO中指的就是判断接口型变量运行时的实际类型

    相关文章

      网友评论

          本文标题:go 反射

          本文链接:https://www.haomeiwen.com/subject/cztqzqtx.html