chaoz的杂货铺

生命有息、学无止境、折腾不止

0%

2022-Golang-反射

反射

go 是静态语言,表示内存中任何一个数据对象(data object)的值及其类型必须是编译期可确定的。因此,go 应用运行时不会像 java 等动态语言一样,在运行期维护所有对象的元数据,以支持多态等需要。也不像 c 语言,不提供任何元数据支持。但注定 go 语言的反射是简单和有限的。

Go语言提供了一种机制在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。

反射是指在程序运行期对程序本身进行访问和修改的能力,程序在编译时变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时程序无法获取自身的信息。

反射是一个强大并富有表现力的工具,能让我们写出更灵活的代码。但是反射不应该被滥用,原因有以下三个。

基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。
大量使用反射的代码通常难以理解。
反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。

https://mp.weixin.qq.com/s/dF89ySHfv6rkhw2qT-jwqA

参考教程

https://www.liwenzhou.com/posts/Go/13_reflect/#autoid-3-1-0

在Go语言中,使用reflect.TypeOf()函数可以获得任意值的类型对象(reflect.Type),程序通过类型对象可以访问任意值的类型信息。
想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。
任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()和Field()方法获得结构体成员的详细信息。

reflect.TypeOf 和 reflect.ValueOf

reflect.TypeOf 和 reflect.ValueOf 两个函数来获取任意对象的 Value 和 Type。

反射在程序中应用非常多,例如:

动态生成数据:json 序列化/反序列化;orm 映射, proxy 透明代理对象
动态调用方法:plugin 实现
框架自动处理程序:annotation tag 注解标签
其他需要数据元数据的应用

参考

https://mp.weixin.qq.com/s/0FNClBKYzgheMiAwjOnKyA

demo

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package main

    import (
    "fmt"
    "reflect"
    )


    func main() {
    //通过反射获取一个变量的值和类型的
    var x float64 = 3.4
    t := reflect.TypeOf(x) //t is reflect.Type
    fmt.Println("type:", t)

    v := reflect.ValueOf(x) //v is reflect.Value
    fmt.Println("value:", v)

    //反射对象与interface对象是可以互相转化的
    var y float64 = v.Interface().(float64)
    fmt.Println("value:", y)

    // 通过反射可以将interface类型变量转换成反射对象,可以使用该反射对象设置其持有的值。
    v1 := reflect.ValueOf(&x)
    v1.Elem().SetFloat(7.1)
    fmt.Println("x :", v1.Elem().Interface())
    }

    ![20220228224114](http://tuchuang.xchcloud.cn/20220228224114.png)
  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package main

    import (
    "fmt"
    "reflect"
    )

    type Student struct {
    Id int
    Name string
    }

    func (s Student) Hello(){
    fmt.Println("我是一个学生")
    }

    func main() {
    s := Student{Id: 1, Name: "咖啡色的羊驼"}

    // 获取目标对象
    t := reflect.TypeOf(s)
    // .Name()可以获取去这个类型的名称
    fmt.Println("这个类型的名称是:", t.Name())

    // 获取目标对象的值类型
    v := reflect.ValueOf(s)
    // .NumField()来获取其包含的字段的总数
    for i := 0; i < t.NumField(); i++ {
    // 从0开始获取Student所包含的key
    key := t.Field(i)

    // 通过interface方法来获取key所对应的值
    value := v.Field(i).Interface()

    fmt.Printf("第%d个字段是:%s:%v = %v \n", i+1, key.Name, key.Type, value)
    }

    // 通过.NumMethod()来获取Student里头的方法
    for i:=0;i<t.NumMethod(); i++ {
    m := t.Method(i)
    fmt.Printf("第%d个方法是:%s:%v\n", i+1, m.Name, m.Type)
    }
    }

20220228224139

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package main

    import (
    "fmt"
    "reflect"
    )

    type Student1 struct {
    Id int
    Name string
    }

    type People struct {
    Student1 // 匿名字段
    }

    func main() {
    p := People{Student1{Id: 1, Name: "咖啡色的羊驼"}}

    t := reflect.TypeOf(p)
    // 这里需要加一个#号,可以把struct的详情都给打印出来
    // 会发现有Anonymous:true,说明是匿名字段
    fmt.Printf("%#v\n", t.Field(0))

    // 取出这个学生的名字的详情打印出来
    fmt.Printf("%#v\n", t.FieldByIndex([]int{0, 1}))

    // 获取匿名字段的值的详情
    v := reflect.ValueOf(p)
    fmt.Printf("%#v\n", v.Field(0))
    }

20220228224357

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package main

    import (
    "fmt"
    "reflect"
    )

    type order struct {
    ordId int
    customerId int
    }

    /*
    反射的三法则:
    从接口值可以反射出反射对象。
    从反射对象可反射出接口值。
    要修改反射对象,其值必须可设置。
    反射的第一条法则是,我们能够吧Go中的接口类型变量转换成反射对象,上面提到的reflect.TypeOf和 reflect.ValueOf 就是完成的这种转换。
    第二条指的是我们能把反射类型的变量再转换回到接口类型,
    最后一条则是与反射值是否可以被更改有关。
    */
    /*
    经过反射后interface{}类型的变量的底层具体类型由reflect.Type表示,底层值由reflect.Value表示。
    reflect包里有两个函数reflect.TypeOf() 和reflect.ValueOf()
    分别能将interface{}类型的变量转换为reflect.Type和reflect.Value。
    这两种类型是创建我们的SQL生成器函数的基础。
    */
    func createQuery(q interface{}) {
    t := reflect.TypeOf(q)
    v := reflect.ValueOf(q)
    fmt.Println("Type ", t)
    fmt.Println("Value ", v)
    }
    func main() {
    o := order{
    ordId: 456,
    customerId: 56,
    }
    createQuery(o)
    }

20220228224437

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package main

    import (
    "fmt"
    "reflect"
    )

    type order1 struct {
    ordId int
    customerId int
    }

    /*
    reflect.Type 表示接口的实际类型,即本例中main.order1
    而Kind表示类型的所属的种类,即main.order是一个「struct」类型,类似的类型map[string]string的Kind就该是「map」
    */
    func createQuery1(q interface{}) {
    t := reflect.TypeOf(q)
    k := t.Kind()
    fmt.Println("Type ", t)
    fmt.Println("Kind ", k)

    }
    func main() {
    o := order1{
    ordId: 456,
    customerId: 56,
    }
    createQuery1(o)

    }

20220228224516

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package main

    import (
    "fmt"
    "reflect"
    )

    type order6 struct {
    ordId int
    customerId int
    }

    /*
    // 获取一个结构体内的字段数量
    NumField() int
    // 根据 index 获取结构体内字段的类型对象
    Field(i int) StructField
    // 根据字段名获取结构体内字段的类型对象
    FieldByName(name string) (StructField, bool)
    */
    func createQuery6(q interface{}) {
    t := reflect.TypeOf(q)
    if t.Kind() != reflect.Struct {
    panic("unsupported argument type!")
    }
    v := reflect.ValueOf(q)
    for i := 0; i < t.NumField(); i++ {
    fmt.Println("FieldName:", t.Field(i).Name, "FiledType:", t.Field(i).Type,
    "FiledValue:", v.Field(i))
    }

    }
    func main() {
    o := order6{
    ordId: 456,
    customerId: 56,
    }
    createQuery6(o)

    }

20220228224604

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    package main

    import (
    "fmt"
    "reflect"
    )

    type order7 struct {
    ordId int
    customerId int
    }

    type employee struct {
    name string
    id int
    address string
    salary int
    country string
    }

    func createQuery7(q interface{}) string {
    t := reflect.TypeOf(q)
    v := reflect.ValueOf(q)
    if v.Kind() != reflect.Struct {
    panic("unsupported argument type!")
    }
    tableName := t.Name() // 通过结构体类型提取出SQL的表名
    sql := fmt.Sprintf("INSERT INTO %s ", tableName)
    columns := "("
    values := "VALUES ("
    for i := 0; i < v.NumField(); i++ {
    // 注意reflect.Value 也实现了NumField,Kind这些方法
    // 这里的v.Field(i).Kind()等价于t.Field(i).Type.Kind()
    switch v.Field(i).Kind() {
    case reflect.Int:
    if i == 0 {
    columns += fmt.Sprintf("%s", t.Field(i).Name)
    values += fmt.Sprintf("%d", v.Field(i).Int())
    } else {
    columns += fmt.Sprintf(", %s", t.Field(i).Name)
    values += fmt.Sprintf(", %d", v.Field(i).Int())
    }
    case reflect.String:
    if i == 0 {
    columns += fmt.Sprintf("%s", t.Field(i).Name)
    values += fmt.Sprintf("'%s'", v.Field(i).String())
    } else {
    columns += fmt.Sprintf(", %s", t.Field(i).Name)
    values += fmt.Sprintf(", '%s'", v.Field(i).String())
    }
    }
    }
    columns += "); "
    values += "); "
    sql += columns + values
    fmt.Println(sql)
    return sql
    }

    //这个SQL生成器完整的实现和测试代码如下
    //https://mp.weixin.qq.com/s/0FNClBKYzgheMiAwjOnKyA
    func main() {
    o := order7{
    ordId: 456,
    customerId: 56,
    }
    createQuery7(o)

    e := employee{
    name: "Naveen",
    id: 565,
    address: "Coimbatore",
    salary: 90000,
    country: "India",
    }
    createQuery7(e)
    }

20220228224637

喜欢这篇文章?打赏一下作者吧!

欢迎关注我的其它发布渠道