V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hetiansu5
V2EX  ›  推广

Reflect-Go 一分钟快速入门

  •  
  •   hetiansu5 · 2020-01-10 16:54:29 +08:00 · 3560 次点击
    这是一个创建于 1804 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Reflect

    本文侧重讲解 reflect 反射的实践应用,适合新手初窥门径。

    reflect 两个基本功能

    • reflect.TypeOf() 动态获取输入数据的类型
    • reflect.ValueOf() 动态获取输入数据的值
    func TypeOf(i interface{}) Type
    func ValueOf(i interface{}) Value
    

    通过 reflect.Type 和 reflect.Value 支持的方法,可以对输入的动态数据进行解析。 那么了解 reflect.Type 和 reflect.Value 提供的方法尤为重要,因为比较多,此介绍放在文末。

    reflect.Kind

    在 reflect.Type 和 reflect.Value 上调用 Kind()方法,可以得到 reflect.Kind 类型值,从而知道动态数据的类型。这个是动态解析的钥匙,通过反射后拿到具体的类型才能做相应的工作,逐层解析。

    const (
    	Invalid Kind = iota
    	Bool
    	Int
    	Int8
    	Int16
    	Int32
    	Int64
    	Uint
    	Uint8
    	Uint16
    	Uint32
    	Uint64
    	Uintptr
    	Float32
    	Float64
    	Complex64
    	Complex128
    	Array
    	Chan
    	Func
    	Interface
    	Map
    	Ptr
    	Slice
    	String
    	Struct
    	UnsafePointer
    )
    

    解析流程

    • Ptr 指针和 Interface 通过调用 Elem()方法得到指向元素的值,再进入循环解析。
    • Slice、Array、Map、Struct 复合数据类型,通过各自的方法拿到他们组成类型值,再进入循环解析
    • 直到为基础数据类型或者 Func、Chan、UnsafePointer。 reflect 解析动态数据流程

    具体类型解析

    下述 demo 中默认

    rt = reflect.TypeOf(data)
    rv = reflect.ValueOf(data)
    

    Struct

    //遍历结构体的 Field
    for i := 0; i < rt.NumField(); i++ {
        rt.Field(i) //第 i 个 Field 的 StructField
        rv.Filed(i) //第 i 个 Field 的 Value
    }
    
    //遍历结构体的方法
    for i := 0; i < rt.NumMethod(); i++ {
        rt.Method(i) //第 i 个方法的 Method
        rv.Method(i) //第 i 个方法的 Value
    }
    

    Map

    //遍历 Map 的 key 和 value
    for _, key := range rv.MapKeys() {
        //其中 key 为键的 Value
        rv.MapIndex(key) //键值的 Value
    }
    
    rv.Len() //map 的大小
    rv.SetMapIndex() //map 索引赋值
    

    Slice Array

    //遍历 Slice 或者 Array
    for i := 0; i < rv.Len(); i++ {
        rv.Index(i) //子元素的 Value
    }
    
    rv.Cap()    //获取切片或者 Array 容量
    rv.SetCap(i) //调整切片容量
    rv.Slice(i, j) //返回切片 s[i:j]的 Value
    

    Ptr Interface

    if !rv.IsNil() {
        rv.Elem() //指针或者 interface 实际指向元素
    }
    

    Bool Int Unit Float Complex String

    rv.Bool() //返回 bool
    rv.Int() //返回 int64
    rv.Uint() //返回 uint64
    rv.Float() //返回 float64
    rv.Complex() //返回 complex128
    rv.Sting() //返回 string
    
    //修改 Value 结构中的值,必须类型一致
    rv.Set()
    rv.SetBool() 
    rv.SetInt() 
    rv.SetUint() 
    rv.SetFloat() 
    rv.SetComplex() 
    rv.SetString()
    rv.SetBytes()
    

    Func Chan UnsafePointer

    动态解析的时候,用得比较少,这里就不说了

    Type 和 Value 方法

    Type 和 Value 拥有的同名方法

    | Method | Type 返回类型 | Value 返回类型 | 备注 | | :---- | :---- | :---- | :---- | | Kind | Kind | Kind | 返回指定对象的 Kind 类型 | | NumMethod | int | int | 返回 struct 拥有的方法总数,包括 unexported 方法 | | MethodByName | Method | Value | 根据方法名找方法 | | Method | Method | Value | 返回第 i 个方法 | | NumField | int | int | 返回 struct 所包含的 field 数量 | | Field | StructField | Value |取 struct 结构的第 n 个 field | | FieldByIndex | StructField | Value |嵌套的方式取 struct 的 field,比如 v.FieldByIndex([]int{1,2})等价于 v.field(1).field(2) | | FieldByName | StructFiel,bool | Value | 返回名称匹配 match 函数的 field | | FieldByNameFunc | StructField,bool | Value | 返回名称匹配 match 函数的 field |

    Type 独有的方法

    | Method | 备注 | | :---- | :---- | |Align |分配内存时的内存对齐字节数 | |FieldAlign |作为 struct 的 field 时内存对齐字节数 | |Name |type 名 string 类型 | |PkgPath |包路径, "encoding/base64", 内置类型返回 empty string | |Size |该类型变量占用字节数 | |String |type 的 string 表示方式 | |Implements |判断该类型是否实现了某个接口 | |AssignableTo |判断该类型能否赋值给某个类型 | |ConvertibleTo |判断该类型能否转换为另外一种类型 | |Comparable |判断该类型变量是否可以比较 | |ChanDir |返回 channel 的方向 recv/send/double | |IsVariadic |判断函数是否接受可变参数 | |Elem |取该类型的元素 | |In |函数第 n 个入参 | |Out |函数第 n 个出参 | |NumIn |函数的入参数个数 | |NumOut |函数的出参个数 | |Key |返回 map 结构的 key 类型 Type | |Len |返回 array 的长度 |

    Value 独有的方法

    | Method | 备注 | | :---- | :---- | |Addr | v 的指针,前提时 CanAddr()返回 true | |Bool | 取值,布尔类型 | |Bytes | 取值,字节流 | |Call |调用函数 | |CallSlice |调用具有可变参的函数 | |CanAddr |判断能否取址 | |CanInterface |判断 Interface 方法能否使用 | |CanSet |判断 v 的值能否改变 | |Cap |判断容量 Array/Chan/Slice | |Close |关闭 Chan | |Complex | 取值,复数 | |Convert |返回将 v 转换位 type t 的结果 | |Elem | 返回 interface 包含或者 Ptr 指针的实际值 | |Float | 取值,浮点型 | |Index 索引操作 | Array/Slice/String | |Int | 取值,整型 | |Interface |将当前 value 以 interface{}形式返回 | |IsNil |判断是否为 nil,chan, func, interface, map, pointer, or slice value | |IsValid |是否是可操作的 Value,返回 false 表示为 zero Value | |Len |适用于 Array, Chan, Map, Slice, or String | |MapIndex |对 map 类型按 key 取值 | |MapKeys |map 类型的所有 key 的列表 | |OverflowComplex | 溢出判断 | |OverflowFloat | 溢出判断 | |OverflowInt | 溢出判断 | |OverflowUint | 溢出判断 | |Pointer |返回 uintptr 适用于 slice | |Recv | chan 接收 | |Send | chan 发送 | |Set | 将 x 赋值给 v,类型要匹配 | |SetBool | Bool 赋值,需要先判断 CanSet()为 true | |SetBytes | Bytes 赋值 | |SetCap | slice 调整切片容量 | |SetMapIndex | map 索引赋值 | |SetUint | Unit 赋值 | |SetPointer |unsafe.Pointer 赋值 | |SetString | String 赋值 | |Slice | return v[i:j] 适用于 Array/Slict/String | |String | return value 的 string 表示方法 | |TryRecv | chan 非阻塞接收 | |TrySend | chan 非阻塞发送 | |Type | 返回 value 的 Type | |UnsafeAddr | 返回指向 value 的 data 的指针 |

    引用链接

    Type 和 Value 方法: https://www.cnblogs.com/ksir16/p/9040656.html

    6 条回复    2020-01-13 09:57:04 +08:00
    hetiansu5
        1
    hetiansu5  
    OP
       2020-01-10 16:55:19 +08:00
    v2ex 的 markdown 不支持居然还不支持表格
    guonaihong
        2
    guonaihong  
       2020-01-11 14:36:42 +08:00
    难道,遇到和我一样喜欢玩 reflect 包的童鞋。。。
    guonaihong
        3
    guonaihong  
       2020-01-11 14:37:10 +08:00
    写错了,是难得。。。
    Aether
        4
    Aether  
       2020-01-11 15:23:27 +08:00
    我觉得所谓“实践运用”,不是罗列一堆枯燥的知识点,就像是把工具箱里的工具摆了一地,但却让人看得发呆。而是从修改椅子上一个转角开始,以一个常见的实例开始,讲述入手的思路,解决的方法选择,最后推导出为什么要使用一种效率更优美的方案,延展引入工具的讲解和实际操作,最后将问题加以解决。

    要做到这一点很难。
    hetiansu5
        5
    hetiansu5  
    OP
       2020-01-13 09:54:21 +08:00
    @guonaihong 嘿嘿。因为撸一个 URL Query String 的编码和解码器,顺便加深一下。
    hetiansu5
        6
    hetiansu5  
    OP
       2020-01-13 09:57:04 +08:00
    @Aether 嗯,现在还达不到这样的功力,一些粗浅的理解。蛮尝试写出来,也希望可以和大家多多交流。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1022 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:46 · PVG 04:46 · LAX 12:46 · JFK 15:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.