go 的反射 reflect

OOM_Killer · · 650 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。
grpc 的 golang 版本也是靠反射实现的。

reflect 的基本功能

  • reflect.TypeOf 返回类型 reflect.Type
  • reflect.ValueOf 返回值 reflect.Value
  • 可以从reflect.Value 中获取类型
  • 通过Kind 来判断类型

先看第一个例子

一 、 反射的基本用法

ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0
TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil

// 反射的基础用法
func base00() {
    var num = 1.2345
    fmt.Println("type: ",reflect.TypeOf(num))       
    fmt.Println("value: ",reflect.ValueOf(num))     
}

运行结果:
type : float64
value : 1.234
二 、 反射来判断参数类型

以下两种方法可以判断 传入参数的类型,动态进行判断。

func base01(v interface{}) {
    t := reflect.TypeOf(v)
    switch t.Kind() {
    case reflect.Float32,reflect.Float64:
        fmt.Println("float")
    case reflect.Int16,reflect.Int32,reflect.Int64:
        fmt.Println("int")
    case reflect.String:
        fmt.Println("string")
    default:
        fmt.Println("none")
    }

    switch v.(type) {   // 方法二
    case float64,float32:
        fmt.Println("float")
    case int32,int64:
        fmt.Println("int")
    case string:
        fmt.Println("none")
    default:
        fmt.Println("none")
    }
}
三 、 通过interface 获取到真实的值

当执行reflect.ValueOf(interface)之后,就得到了一个类型为”relfect.Value”变量,可以通过它本身的Interface()方法获得接口变量的真实内容,然后可以通过类型判断进行转换,转换为原有真实类型。
其对类型要求是什么严格的。

// 已知原有类型进行强制类型转换
func base02() {
    var num = 1.2345
    pointer := reflect.ValueOf(&num)
    value := reflect.ValueOf(num)

    // 转换类型不匹配会直接 panic
    convertPointer := pointer.Interface().(*float64)
    convertValue   := value.Interface().(float64)


    fmt.Println(convertPointer)       
    fmt.Println(convertValue)         
}

执行结果:
0xc000098010
1.2345
四 、通过反射来遍历一个结构体,其方法,其feild
  1. 先获取interface的reflect.Type,然后通过NumField进行遍历
  2. 再通过reflect.Type的Field获取其Field
  3. 最后通过Field的Interface()得到对应的value
type User struct {
    Id      int
    Name    string
    Work    Worker
}

type Worker struct {
    Id           int
    Occupation   string
}

func (u User) ReflectCallFunc() {
    fmt.Printf("My Id :%d ,My Name :%s ,My Occupation :%s",u.Id,u.Name,u.Work.Occupation)
}

func (u User) ReflectCallFuncHasArgs(foo int) {
    fmt.Printf("This is number %d \n", foo)
}


// 遍历类型探测其值
func base03(input interface{}) {
    getType := reflect.TypeOf(input)
    fmt.Println("get type is: ",getType.Name())  // get type is:  User

    getValue := reflect.ValueOf(input)
    fmt.Println("get all Fields is ", getValue) // get all Fields is  {1 ZhangSan {2 Programmer}}

    // 对字段进行遍历 获取方法的字段
    for i := 0;i < getType.NumField();i++ {
        field := getType.Field(i)
        value := getValue.Field(i).Interface()
        fmt.Printf("%s: %v = %v \n",field.Name,field.Type,value)
    }

    /*
        Id: int = 1
        Name: string = ZhangSan
        Work: main.Worker = {2 Programmer}
    */

    // 获取方法
    for i := 0;i < getType.NumMethod();i++ {
        m := getType.Method(i)
        fmt.Printf("%s : %v\n",m.Name,m.Type)
    }

    /*
        ReflectCallFunc : func(main.User)
        ReflectCallFuncHasArgs : func(main.User, int)
    */
}
五 、 通过反射来重新赋值

reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是“addressable”的。

// 通过反射设置变量的值
func base04() {
    var num = 1.234
    fmt.Println("old value of pointer: ",num) // old value of pointer:  1.234

    // 获取其指针(必须是指针)
    pointer := reflect.ValueOf(&num)
    newValue := pointer.Elem()

    fmt.Println("type of pointer:",newValue.Type()) // type of pointer: float64
    fmt.Println("settability of pointer",newValue.CanSet()) // settability of pointer true

    // 重新赋值
    newValue.SetFloat(3.1415)
    fmt.Println("new value of pointer: ",num) // old value of pointer:  1.234
}
六、通过反射调用方法

我们可以利用反射来编写灵活的代码
按名字访问结构成员
reflect.ValueOf(e).FieldByName("Name")
按名字访问结构方法
reflect.ValueOf(
e).MethodByName("FuncName").Call([]reflect.Value{reflect.ValueOf(1)})

// 通过反射调用方法
func base05(input interface{}) {
    getValue := reflect.ValueOf(input)

    // 有参数调用
    methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
    args := []reflect.Value{reflect.ValueOf(2)}
    methodValue.Call(args)

    // 无参数调用
    methodValue = getValue.MethodByName("ReflectCallFunc")
    args = make([]reflect.Value,0)
    methodValue.Call(args)
}

七、判断一个参数是不是指针
kind 可以进行类型判断, 加上 string() 方法,就可以将类型进行字符串打印。

// 最简单调用
func base06(input interface{}) {
    k := reflect.TypeOf(input).Kind().String()
    fmt.Println(k)  

    t := reflect.TypeOf(input).Elem()
    v := reflect.ValueOf(input).Elem()
    fmt.Println(t)  
    fmt.Println(v)
}

执行结果
ptr
main.User
{1 ZhangSan {2 Programmer}}

八、记一类特殊的反射

在对json数据的序列化,反序列化的时候,经常会看到如下的代码

type Config struct {
    Id         int       `bson:"id" json:"id"`
    Created    time.Time `bson:"created" json:"created"`
    Updated    time.Time `bson:"updated" json:"updated"`
    Creator    string    `bson:"creator" json:"creator"`
    Updater    string    `bson:"updater" json:"updater"`
}

struct tag ,在struct field 的后面有一个"format:normal"格式的 tag。其实在解析时,这些东西就是用反射去解析的。
如下面的示例

type Employee struct {
    EmployeeID string
    Name       string `format:"normal"`
    Age        int
}

func base07() {
    e := &Employee{"1", "Mike", 30}
    //按名字获取成员
    fmt.Printf("Name: value(%[1]v), Type(%[1]T) \n", reflect.ValueOf(*e).FieldByName("Name"))
    if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
        fmt.Println("Failed to get 'Name' field.")
    } else {
        // 获取 tag中值
        fmt.Println("Tag:format", nameField.Tag.Get("format"))
    }
}

执行结果:
Name: value(Mike), Type(reflect.Value) 
Tag:format normal

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:OOM_Killer

查看原文:go 的反射 reflect

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

650 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传