zoukankan      html  css  js  c++  java
  • golang学习笔记---reflect包

    go语言提供了一种机制,在编译时不知道类型的情况下,可更新变量,在运行时查看值,调用方法以及直接对他们的布局进行操作。这种机制称为反射(reflection)。

    为什么使用反射

      有时候我们需要写一个函数有能力统一处理各种值类型的函数,而这些类型可能无法共享同一个接口,也可能布局未知,也有可能这个类型在我们设计函数时还不存在。甚至这个类会同时存在上面三个问题。假设我们设计一个例子来判断,如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package main
     
    import "fmt"
     
    type stringer interface {
        Stringer() string
    }
     
    func Sprint(x interface{})  {
        switch x := x.(type) {
        case stringer:
            fmt.Println(x, "is stringer")
        case string:
            fmt.Println(x, "is string")
        case int:
            fmt.Println("int")
        default:
            fmt.Println("其他类型")
        }
    }
     
    func main()  {
        Sprint("fd")
    }

     这时你会发现,如果类型很多或者更多不确定的类型就会很麻烦。所以我们引入了reflect包。

    reflect

      反射功能由reflect包提供,它定义了两个重要的类型:Type和Value,并且TypeOf和ValueOf

    • Type: 这是一个接口,真正使用该接口的实例是reflect.rtype,该实例持有动态类型的所有信息。并且提供下面方法
      • kind(): 获取具体类型的底层类型。
      • Elem(): 这个方法返回的是原始变量的元素的类型。
    • Value: 这是一个struct,持有动态值的所有信息。
      • Type(): Type方法返回接口变量的动态类型信息,也就是传入ValueOf方法的原始变量的类型。
      • Kind(): 与Type的kind方法一样,返回的是原始类型。
      • Interface():把一个reflect.Value对象还原回一个空接口类型的变量,可以通过类型断言:x, ok := v.Interface().(int) 
      • Elem(): 调用该方法的Value对象

     type.kind() &type.name()

    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
    package main
     
    import (
        "fmt"
        "reflect"
    )
     
    type myInt int64
     
    func reflectType(x interface{}) {
        t := reflect.TypeOf(x)
        fmt.Printf("type:%v kind:%v ", t.Name(), t.Kind())
    }
     
    func main() {
        var a *float32 // 指针
        var b myInt    // 自定义类型
        var c rune     // 类型别名//代表int32
        reflectType(a) // type: kind:ptr
        reflectType(b) // type:myInt kind:int64
        reflectType(c) // type:int32 kind:int32
     
        type person struct {
            name string
            age  int
        }
            var d = person{
            name: "wang",
            age:  18,
        }
         
        reflectType(d) // type:person kind:struct
    }       

    TypeOf

      TypeOf函数接收任何的interface{}参数,并且把接口中的动态类型以reflect.Type形式返回。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package main
     
    import (
        "fmt"
        "reflect"
    )
     
    func reflectType(x interface{}) {
        v := reflect.TypeOf(x)
        fmt.Printf("type:%v ", v)
    }
    func main() {
        var a float32 = 3.14
        reflectType(a) // type:float32
        var b int64 = 100
        reflectType(b) // type:int64
    }

    ValueOf 

      ValueOf函数可以接收任意的interface{}并将接口的动态值以reflect.Value的形式返回。与reflect.TypeOf类似,reflect.ValueOf的返回值也是具体值,不过reflect.Value也可以包含一个接口值。

      对于不同类型,我们用reflect.Value的kind方法来区分不同类型。但类型的分类(kind)只有少数几种:

    • 基础类型: Bool, String以及数字类型
    • 聚合类型:Array, struct
    • 引用类型:Chan, Func, Ptr, Slice, Map
    • 接口类型:interface
    • Invalid类型:表示没有任何值。reflect.Value的零值就属于Invalid类型。
    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"
    )
     
    func reflectValue(x interface{}) {
        v := reflect.ValueOf(x)
        k := v.Kind()
        switch k {
        case reflect.Int64:
            // v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
            fmt.Printf("type is int64, value is %d ", int64(v.Int()))
        case reflect.Float32:
            // v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
            fmt.Printf("type is float32, value is %f ", float32(v.Float()))
        case reflect.Float64:
            // v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
            fmt.Printf("type is float64, value is %f ", float64(v.Float()))
        }
    }
    func main() {
        var a float32 = 3.14
        var b int64 = 100
        reflectValue(a) // type is float32, value is 3.140000
        reflectValue(b) // type is int64, value is 100
        // 将int类型的原始值转换为reflect.Value类型
        c := reflect.ValueOf(10)
        fmt.Printf("type c :%T ", c) // type c :reflect.Value
    } 

    value.Elem设置值

      上面知识获取了值及类型,如果想要修改,可以通过elem,但必须传递的是指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package main
     
    import (
        "fmt"
        "reflect"
    )
     
    func reflectSetValue2(x interface{}) {
        v := reflect.ValueOf(x)
        // 反射中使用 Elem()方法获取指针对应的值
        if v.Elem().Kind() == reflect.Int64 {
            v.Elem().SetInt(200)
        }
    }
    func main() {
        var a int64 = 100
        reflectSetValue2(&a)
        fmt.Println(a)
    }

     最后整理一下常用的类型判断,如 

    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
    func Dis(path string, v reflect.Value)  {
        switch v.Kind() {
        case reflect.Invalid:  // 空
            fmt.Printf("%s= Invalid ", path)
        case reflect.Slice, reflect.Array:
            for i := 0; i<v.Len(); i++{
                Dis(fmt.Printf("%s[%d]", path, i), v.Index(i))
            }
        case reflect.Struct:
            for i := 0; i< v.NumField(); i++{
                feildPath := fmt.Sprintf("%s.%s",path, v.Type().Field(i).Name)
                Dis(feildPath, v.Field(i))
            }
        case reflect.Map:
            for _, key :=range v.MapKeys(){
                fmt.Printf("%s", v.MapIndex(key))
            }
        case reflect.Ptr:
            if v.IsNil(){
                fmt.Printf("%s= nil ", path)
            }else {
                Dis(fmt.Sprintf("*%s",path),v.Elem())
            }
        case reflect.Interface:
            if v.IsNil(){
                fmt.Printf("%s=nil ", path)
            }else {
                fmt.Printf("%s.type=%s ",path, v.Elem().Type())
            }
        default// 基础类型,chan, 函数
            fmt.Printf("%s ",path)
             
             
             
             
         
        }
    }

      

     

     

  • 相关阅读:
    让GoogleCode的SVN下的HTML文件在FireFox下正常显示
    添加验证控件出错
    【转载】SQLServer中char、varchar、nchar、nvarchar的区别:
    人生第一篇博客
    二叉排序树
    最小编辑距离
    面试题集锦_4
    面试题集锦_3
    键树
    B树
  • 原文地址:https://www.cnblogs.com/saryli/p/13402233.html
Copyright © 2011-2022 走看看