• Go语言 8 反射


    文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/

    Go学习群:415660935

    8.1概念和作用

    Reflection(反射)在计算机中表示程序能够检查自身结构的能力,它是元编程的一种形式。通过反射,可以获取丰富的类型信息,并可以利用这些类型信息做非常灵活的工作。

    通过反射机制在运行时能够完成如下功能:

    1.确认对象的类

    2.确认对象的类型的所有成员变量和方法

    3.动态调用对象的方法

    8.2 基本用法

    本质上来说,反射就是一种检查接口变量的类型和值的机制。在Go语言中使用反射,首先要了解两个基本类型—reflect.Typereflect.Value。对任何变量进行反射,都可以得到一个包含TypeValue的信息结构。顾名思义,Type主要表达的是被反射的这个变量本身的类型信息,而Value则为该变量的值。

    1. 反射第一定律:反射可以将“接口类型对象”转换为“反射类型对象”

     

    以上两个函数的参数都是接口类型,可以接收任何类型的变量。所以,称为接口类型变量。返回值是TypeValue,这两个类型称为反射类型,所以通过这两个函数可以将“接口类型变量”即参数转换为反射类型对象即返回值。

    2 反射第二定律:反射可以将“反射类型对象”转换为“接口类型对象”

     

    根据一个 reflect.Value 类型的变量,我们可以使用 Interface( )方法恢复其接口类型的值。事实上,这个方法会把 type value 信息打包并填充到一个接口变量中,然后返回。

    这两个定律互为逆操作。案例如下:

    // reflect.go

    package main

     

    import (

        "fmt"

        "reflect"

    )

     

    func main() {

        // 1 根据接口类型的变量获取反射类型对象

        var pi float64 = 3.14

        t := reflect.TypeOf(pi)

        v := reflect.ValueOf(pi)

        fmt.Println(t, v)

        // 2 根据反射类型对象获取接口类型变量

        pi = v.Interface().(float64)

        fmt.Println(pi)

        

    }

    3 反射第三定律:如果要修改“反射类型对象”,其值必须是“可写的”

    通过反射定律一可以知道,反射对象包含了接口变量中存储的值以及类型。如果反射对象中包含的值是原始值,那么可以通过反射对象修改原始值;如果反射对象中包含的值不是原始值(反射对象包含的是副本值或指向原始值的地址),那么该反射对象是不可以修改的。

     

    package main

     

    import (

        "fmt"

        "reflect"

    )

    func main() {

        var num int = 10

        fmt.Println(num)

        /*

            // 3.1

            v := reflect.ValueOf(num)

            // 将num作为参数传递给ValueOf后,执行SetInt函数,num的值没有改变

            v.SetInt(100)

        */

        /*

            // 3.2 将num作为参数传递给ValueOf后,执行SetInt函数,修改的是num的地址,不是num的值

            v := reflect.ValueOf(&num)

            v.SetInt(100)

        */

        // 3.3 调用Elem(),相等于解引用 *(&num)

        v := reflect.ValueOf(&num).Elem()

        v.SetInt(100)

        fmt.Println(num)

     

    }

    4 对结构体的反射操作

    4.1 遍历结构体的成员并修改成员的值

    package main

     

    import (

        "fmt"

        "reflect"

    )

     

    type Person struct {

        Name string

        Age  int

    }

     

    func main() {

     

        // 1 p1是对象

    p1 := Person{"李四", 20}

    v := reflect.ValueOf(&p1).Elem()

     

            // 1 p1是对象的地址

        //p1 := &Person{"李四", 20}

        //v := reflect.ValueOf(p1).Elem()

     

        num := v.NumField()

        for i := 0; i < num; i++ {

            fmt.Println(v.Type().Field(i).Name,

                v.Field(i).Type(), v.Field(i).Interface())

        }

        //修改结构体中字段的值

        v.Field(0).SetString("王五")

        v.Field(1).SetInt(30)

        fmt.Println(p1)

    }

    4.2 遍历结构体的方法并调用方法

    // reflect.go

    package main

     

    import (

        "fmt"

        "reflect"

        "strconv"

    )

     

    type Person struct {

        Name string

        Age  int

    }

     

    func (p *Person) SetName(name string) {

        p.Name = name

    }

    func (p *Person) SetAge(age int) {

        p.Age = age

    }

    func (p *Person) ToString() string {

        return "Name:" + p.Name + "Age:" + strconv.Itoa(p.Age)

    }

     

    func main() {

     

        // 5 打印和调用结构体的方法

    p2 := &Person{"李四", 20}

    // 方法的接收器是指针,如果方法的接收器是对象不需要取地址

        pv := reflect.ValueOf(&p2).Elem()

        num := pv.NumMethod()

        fmt.Println(num)

        for i := 0; i < num; i++ {

            fmt.Println(pv.Type().Method(i).Type) //方法类型

            fmt.Println(pv.Type().Method(i).Name) //方法名称

        }

        params := make([]reflect.Value, 1)

        params[0] = reflect.ValueOf(35)

        pv.Method(0).Call(params)

        params[0] = reflect.ValueOf("张飞")

        pv.Method(1).Call(params)

        info := pv.MethodByName("ToString").Call(nil)

        fmt.Println(info[0])

     

    }

    疑问:反射结构体的方法时,采用结构体指针变量的地址作为ValueOf参数可以遍历到接收器为变量的指针的方法和为变量本身的方法。但是使用采用结构体指针变量作为ValueOf参数只能遍历到接收器为变量本身的方法。

    5反射的综合例子:

    // reflect.go

    package main

     

    import (

        "fmt"

        "reflect"

        "strconv"

    )

     

    type Person struct {

        Name string

        Age  int

    }

     

    func (p *Person) SetName(name string) {

        p.Name = name

    }

    func (p *Person) SetAge(age int) {

        p.Age = age

    }

    func (p *Person) ToString() string {

        return "Name:" + p.Name + "Age:" + strconv.Itoa(p.Age)

    }

     

    // 1 根据接口类型的对象获取反射类型对象

    func PrintInfo(object interface{}) (v reflect.Value) {

        t := reflect.TypeOf(object)

        v = reflect.ValueOf(object)

        fmt.Println(t, v)

        return v

    }

     

    // 2 根据反射类型对象获取接口类型对象

    func ValueToObject(v reflect.Value) interface{} {

        return v.Interface()

     

    }

    func main() {

     

        // 1 根据接口类型对象获取反射类型对象

        var pi float64 = 3.14

        v1 := PrintInfo(pi)

        var p1 = Person{"张三", 18}

        v2 := PrintInfo(p1)

        fmt.Printf("%p ", &p1)

     

        // 2 根据反射类型对象Value获取接口类型对象

        pi = ValueToObject(v1).(float64)

        fmt.Println(pi)

        p1 = ValueToObject(v2).(Person)

        fmt.Println(p1)

        p1.ToString()

        fmt.Printf("%p ", &p1)

     

        // 3 如果要修改反射类型对象,其值必须是可写的

        var num int = 10

        fmt.Println(num)

        v := reflect.ValueOf(&num).Elem()

        v.SetInt(100)

        fmt.Println(num)

     

        // 4 打印和修改结构体中字段的信息

        p2 := &Person{"李四", 20}

        v = reflect.ValueOf(p2).Elem()

        num = v.NumField()

        for i := 0; i < num; i++ {

            fmt.Println(v.Type().Field(i).Name,

                v.Field(i).Type(), v.Field(i).Interface())

        }

        //修改结构体中字段的值

        v.Field(0).SetString("王五")

        v.Field(1).SetInt(30)

        fmt.Println(*p2)

     

        // 5 打印和调用结构体的方法

        pv := reflect.ValueOf(&p2).Elem()

        num = pv.NumMethod()

        fmt.Println(num)

        for i := 0; i < num; i++ {

            fmt.Println(pv.Type().Method(i).Type) //方法类型

            fmt.Println(pv.Type().Method(i).Name) //方法名称

        }

        params := make([]reflect.Value, 1)

        params[0] = reflect.ValueOf(35)

        pv.Method(0).Call(params)

        params[0] = reflect.ValueOf("张飞")

        pv.Method(1).Call(params)

        info := pv.MethodByName("ToString").Call(nil)

        fmt.Println(info[0])

     

    }

  • 相关阅读:
    C#实现将字符串转换成代码并执行
    Net实现钩子函数(Hook)以及通过SendMessage实现自动点击按钮和给文本框赋值
    异步与多线程的区别
    使用NODEJS实现JSONP的实例
    JS闭包作用域解析
    InterLocked学习笔记
    C#方法中的各类参数
    C# 数据类型详解以及变量、对象与内存
    通过Performance Monitor观察程序内存使用情况
    Git学习笔记(windows git之初体验)
  • 原文地址:https://www.cnblogs.com/mazg/p/8275204.html
Copyright © 2020-2023  润新知