Go reflect应用

Posted by 爱折腾的工程师 on Friday, March 8, 2019

简介

推荐先阅读下来自Go官方Blog的laws-of-reflection,Go语言创始人之一Rob Pike写的.

Reflection: 反射是程序检查自身结构,类型的一种能力,它是元编程的一种形式。

types和interfaces

Go中的types,因为reflect是基于types构建,Go是静态类型,每个变量在编译时都有固定的类型,举个例来看:

type TestInt int
var i int
var j TestInt

变量i,j的静态类型是不同的,尽管它们的基础类型是一样的,如果没有转换的话,它们之间不能互相赋值。

Go中的interface,interface类型是一种重要的类型,interface变量可以存放任何具体变量(非interface),只要实现这个接口的所有方法。 举例来看:

io库中的io.Reader和io.Writer

// Implementations must not retain p.
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Implementations must not retain p.
type Writer interface {
    Write(p []byte) (n int, err error)
}

io.Reader/io.Writer类型的变量可以接受任何类型具有read/write方法的值,举例来看:

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)

r的类型总是io.Reader

interface类型中有个非常重要的典型:空interface

interface{}

Go中的interface不是动态类型,它们是静态类型,interface类型的变量始终是相同的静态类型,尽管interface变量中的值在运行时可能会改变类型。 interface总是(value,具体类型)的形式,而不是(value,interface类型)的形式

Reflection法则

reflect库,有两个类型:Type、Value,还有两个简单的函数:reflect.TypeOf、reflect.ValueOf

反射从接口值到反射对象

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))
}

# 运行结果
type: float64

查看TypeOf的定义,输入参数是个空interface类型

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}

当调用reflect.TypeOf(x)时,x第一次存储在空interface,然后作为参数传入,reflect.TypeOf解包空interface, 并还原其类型信息. reflect.ValueOf则是还原其value信息.

var x float64 = 3.4
    fmt.Println("value:", reflect.ValueOf(x).String())

运行结果:
value: <float64 Value>

fmt包默认情况下会使用reflect.Value来显示变量的具体值,用String方法则不会进行此操作.

此外还有Kind方法来分类存储的类型:有Uint,Float64,Slice等等

var x float64 = 3.4
    v := reflect.ValueOf(x)
    fmt.Println("type:", v.Type())
    fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
    fmt.Println("value:", v.Float())

运行结果:
type: float64
kind is float64: true
value: 3.4
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())                        // MyInt.
fmt.Println("kind is int: ", v.Kind() == reflect.Int) // true.

Kind判断依旧是reflect.Int,Kind不能识别是来自MyInt的int,尽管静态类型是MyInt

反射从反射对象到接口值

Go反射反转

Interface方法,把type和value信息又重新打包回interface

// Interface returns v's current value as an interface{}.
// It is equivalent to:
//	var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
    return valueInterface(v, true)
}
var x float64 = 3.14
    v := reflect.ValueOf(x)
    fmt.Println(v.Interface())
    fmt.Printf("%7.1e\n", v.Interface())

运行结果:
3.14
3.1e+00

修改反射对象,值必须是可设置

var x float64 = 3.4
    p := reflect.ValueOf(&x) // Note: take the address of x.
    fmt.Println("type of p:", p.Type())
    fmt.Println("settability of p:", p.Elem().CanSet())

运行结果:
type of p: *float64
settability of p: true

要想修改反射对象的值,得把x的地址作为参数传进去,p.Elem()得到p指针真正指向的地方

var x float64 = 3.4
    p := reflect.ValueOf(&x) // Note: take the address of x.
    fmt.Println("type of p:", p.Type())
    v := p.Elem()                     x
    fmt.Println("settability of p:", v.CanSet())
    v.SetFloat(7.1)
    fmt.Println(v.Interface())
    fmt.Println(x)

运行结果:
type of p: *float64
settability of p: true
7.1
7.1

修改Struct的值

type T struct {
    A int
    B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    fmt.Printf("%d: %s %s = %v\n", i,
        typeOfT.Field(i).Name, f.Type(), f.Interface())
}

运行结果:
0: A int = 23
1: B string = skidoo
type T struct {
    A int
    B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    s.Field(0).SetInt(77)
    s.Field(1).SetString("Sunset Strip")
    fmt.Printf("%d: %s %s = %v\n", i,
        typeOfT.Field(i).Name, f.Type(), f.Interface())
}
fmt.Println("t is now", t)

运行结果:
0: A int = 77
1: B string = Sunset Strip
t is now {77 Sunset Strip}

reflect应用场景

动态无参调用函数

type T struct{}

func main() {
    name := "Do"
    t := &T{}
    reflect.ValueOf(t).MethodByName(name).Call(nil)
}

func (t *T) Do() {
        fmt.Println("hello world")
}

运行结果:
hello world

动态有参调用函数

type T struct{}

func main() {
    name := "Do"
    t := &T{}
    a := reflect.ValueOf("hello")
    b := reflect.ValueOf("world")
    in := []reflect.Value{a, b}
    reflect.ValueOf(t).MethodByName(name).Call(in)
}

func (t *T) Do(v ...string) {
    for _, i := range v {
        fmt.Println("hello " + i)
    }
}

运行结果:
hello hello
hello world

struct tag解析

type T struct {
    A string    `json:"a" test:"ta"`
    B string `json:"b" test:"tb"`
}

func main() {
    t := T{
        A: "a",
        B: "b ",
    }
    tt := reflect.TypeOf(t)
    for i := 0; i < tt.NumField(); i++ {
        field := tt.Field(i)
        if json, ok := field.Tag.Lookup("json"); ok {
            fmt.Println(json)
        }
        test := field.Tag.Get("test")
        fmt.Println(test)
    }
}

运行结果:
a
ta
b
tb

struct类型转换、赋值

type T struct {
    A int    `newT:"AA"`
    B string `newT:"BB"`
}

type newT struct {
    AA int
    BB string
}

func main() {
    t := T{
        A: 123,
        B: "hello",
    }
    tt := reflect.TypeOf(t)
    tv := reflect.ValueOf(t)

    newT := &newT{}
    newTValue := reflect.ValueOf(newT)

    for i := 0; i < tt.NumField(); i++ {
        field := tt.Field(i)
        newTTag := field.Tag.Get("newT")
        tValue := tv.Field(i)
        newTValue.Elem().FieldByName(newTTag).Set(tValue)
    }

    fmt.Println(newT)
}

运行结果:
&{123 hello}

判断实例实现某接口

type TestInterfafce interface {
    test()
}

type T struct {
    A string
}

func (t *T) test() {}

func main() {
    t := &T{}
    TIF := reflect.TypeOf((*TestInterfafce)(nil)).Elem()
    tv := reflect.TypeOf(t)
    fmt.Println(tv.Implements(TIF))
}

运行结果:
true

接口模糊匹配

package main

import (
    "errors"
    "fmt"
    "reflect"
    "strings"
)

func FuzzyFind(sources interface{}, destType reflect.Type, search string, fuzzySearchFields []string) (interface{}, error) {
    v := reflect.ValueOf(sources)
    //判断sources值的类型
    if v.Kind() != reflect.Slice {
        return nil, errors.New("sources kind is not slice")
    }
    //根据目标类型创建reflect slice
    matches := reflect.MakeSlice(destType, 0, 0)
    //遍历v
    for i := 0; i < v.Len(); i++ {
        //获取下标元素的值
        targetValue := v.Index(i)
        //获取下标元素的值的类型
        reType := targetValue.Type()
        //判断下标元素的值的类型是否为指针,以及指针指向的类型是否为结构体
        if reType.Kind() != reflect.Ptr || reType.Elem().Kind() != reflect.Struct {
            return nil, errors.New("sources should be a struct pointer")
        }
        //获取指针指向的结构体的值
        ve := reflect.Indirect(targetValue)
        //遍历结构体的字段
        for i := 0; i < ve.NumField(); i++ {
            //获取结构体字段对应的值
            value := fmt.Sprintf("%v", ve.Field(i))
            for _, fuzzySearchField := range fuzzySearchFields {
                //是否包含search字符串以及对比查询字段名与结构体字段名是否匹配
                if strings.Contains(strings.ToLower(value), strings.ToLower(search)) &&
                    fuzzySearchField == fmt.Sprintf("%v", reType.Elem().Field(i).Name) {
                    //如果一旦有一个字段匹配,就加入reflect slice,然后退出循环
                    matches = reflect.Append(matches, targetValue)
                    break
                }
            }
        }
    }
    //强制类型转换留给外层调用方
    return matches.Interface(), nil
}

type Instance struct {
    Name string
    Owner string
}

func main() {

    a := &Instance{
        Name: "1",
        Owner: "tes",
    }

    b := &Instance{
        Name: "test",
        Owner: "2",
    }

    c := &Instance{
        Name: "test",
        Owner: "ttt",
    }

    d := &Instance{
        Name: "test234",
        Owner: "ttt123",
    }

    list := []*Instance{a, b, c, d}

    tmpSlice := make([]*Instance, 0)

    res, _ := FuzzyFind(list, reflect.TypeOf(tmpSlice), "test", []string{"Name", "Owner"})

    for _, i := range res.([]*Instance) {
        fmt.Println(i.Name, i.Owner)
    }
}

参考链接

「真诚赞赏,手留余香」

爱折腾的工程师

真诚赞赏,手留余香

使用微信扫描二维码完成支付