go语言反射的使用

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

reflect.ValueOf(i interface{})

reflect中ValueOf能够将变量转化为reflect.Value类型

  • reflect.Value 表示变量的实际值,如果接口为空则返回0。

  • reflect.Value类型提供了Kind()方法,获取变量实际的种类。

  • reflect.Value类型提供了Type()方法,获取变量实际的类型,此时可以类比于TypeOf()

var i intvalue := reflect.ValueOf(i)

fmt.Println(value)        // 输出:0fmt.Println(value.Type()) // 输出:intfmt.Println(value.Kind()) // 输出:inttype S struct {
  A string}var s = S{"SSS"}
value2 := reflect.ValueOf(s) // 使用ValueOf()获取到结构体的Value对象fmt.Println(value2)        // 输出:{SSS}fmt.Println(value2.Type()) // 输出:main.Sfmt.Println(value2.Kind()) // 输出:struct复制代码

变量i使用Kind()和Type()两个方法都输出了int,而结构体s的Type()方法输出了S,Kind()方法输出了struct

由此可以总结如下,如果你想拿到结构体的类型的时候,使用Type()方法。如果只是相判断是否是结构体时,就使用Kind()

type MyInt8 int8var num MyInt8 = 1val := reflect.ValueOf(num)
fmt.Println(val.Type()) // main.MyInt8fmt.Println(val.Kind()) // int8复制代码

上面的代码中,虽然变量 v 的静态类型是MyInt,不是 int,Kind 方法仍然返回 reflect.Int。换句话说, Kind 方法不会像 Type 方法一样区分 MyInt 和 int。

setter and getter

Value类型也有一些类似于Int、Float的方法,用来提取底层的数据

var x float64 = 3.4v := reflect.ValueOf(x)

fmt.Println("value:", v.Float()) // 输出:3.4var x uint8 = 'x'v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())       // uint8.fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.x = uint8(v.Uint())    // v.Uint returns a uint64.复制代码

Value 类型的Int 方法返回值为 int64类型,SetInt 方法接收的参数类型也是 int64 类型。实际使用时,可能需要转化为实际的类型

如果setter的函数不是对应的类型会导致程序panic,下面的程序就会panic

var num int8 = 1val := reflect.ValueOf(num)
fmt.Println(val.Uint())// panic: reflect: call of reflect.Value.Uint on int8 Value复制代码

但对于下面的用法并不会panic

type MyInt8 int8var num MyInt8 = 1val := reflect.ValueOf(num)
fmt.Println(val.Int())复制代码
var num float64 = 1.2345// 通过reflect.ValueOf获取num中的reflect.Value,注意,参数必须是指针才能修改其值pointer := reflect.ValueOf(&num)
newValue := pointer.Elem()

fmt.Println(pointer.Type())    // 输出: *float64fmt.Println(newValue.Type())   // 输出: float64fmt.Println(newValue.CanSet()) // 输出: true// 重新赋值newValue.SetFloat(77)
fmt.Println("new value of pointer:", num) // 输出: 77////////////////////// 如果reflect.ValueOf的参数不是指针,会如何?pointer = reflect.ValueOf(num)//newValue = pointer.Elem() // 如果非指针,这里直接panic,“panic: reflect: call of reflect.Value.Elem on float64 Value”复制代码

只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是“addressable”的,(具体原因可以参考《参考资料-1》)

struct中只有暴露到外部的字段才是“可写的”

reflect.Value.Elem() 表示获取原始值对应的反射对象,只有原始对象才能修改,当前反射对象是不能修改的

对于不确定是否是指针的可以使用 reflect.Indirect(value)

from reflection object to interface value

relfect.Value变量,可以通过它本身的Interface()方法获得接口变量的真实内容,然后可以通过类型判断进行转换,转换为原有真实类型

y := v.Interface().(float64) // y will have type float64.fmt.Println(y)复制代码

在不确定类型的时刻

var num int8 = 1val := reflect.ValueOf(&num).Elem()var s stringswitch val.Interface().(type) {  case int, int8, int32, int64:
  s = strconv.FormatInt(val.Int(), 10)
}

fmt.Println(s)复制代码

这样也可以

var s stringswitch val.Kind() {  case reflect.Int8:
  s = strconv.FormatInt(val.Int(), 10)
}复制代码

struct

对于标量类型可以使用类似v.Uint()的函数直接获取到实际值,对于struct类型的来说,如果是已知的类型可以使用类型断言(ValueOf().Interface().(struct))来获取实际值。

field

对于未知的struct需要可以使用如下的方式遍历结构的每一个字段

type S struct {
  A string
  B int}var s = S{"SSS", 1}
val := reflect.ValueOf(s)for i := 0; i < val.NumField();i++{
  fmt.Println(val.Field(i))
}复制代码

其中

val.NumField() 可以获取结构体字段数

val.Field(i int) Value 可以获取第i个字段

当然可以使用另外两个函数

type S struct {
  A string
  B int
  C struct {
    D []int
  }
}var s = S{"SSS", 1, struct{ D []int }{[]int{1, 2, 3}}}
val := reflect.ValueOf(s)

fmt.Println(val.FieldByIndex([]int{2, 0})) // 输出: [1 2 3] (获取第2个字段里的第0个字段) fmt.Println(val.FieldByName("B"))          // 输出: 1复制代码

method

对于结构体类型还会涉及到结构体所属方法(函数),使用方式和获取字段类似

for i := 0; i < val.NumMethod(); i++ {
  fmt.Println(val.Method(i))
}复制代码

对函数,那么自然可以进行调用,使用call 函数,它的参数就是函数参数的切片,返回的也是返回值切片

type S struct {
	A string
	B int `json:"tag"`}func (t S) Mth(p string) (string, int) {	return t.A + p, t.B
}

s := S{"SSS", 1}

val := reflect.ValueOf(s)
m2 := val.MethodByName("Mth")

params := []reflect.Value{reflect.ValueOf("AAA")}
fmt.Println(m2.Call(params)[0].String()) // 输出: SSSAAA复制代码

上面所有函数对于非struct都会panic

对于call来说,传入的slice个数需要和函数的个数匹配,否则也会panic

reflect.TypeOf(i interface{})

reflect.TypeOf可以将变量转化为reflect.Type类型,

  • reflect.Type 表示变量的实际类型,如果接口为空则返回nil。此时可以类比于ValueOf().Type()

  • relfect.Type 提供了Kind()方法,获取变量的种类。

var i intvalue := reflect.TypeOf(i)

fmt.Println(value2)       // 输出: intfmt.Println(value.Type()) // 输出: value.Type undefined (TypeOf 就没有Type函数了)fmt.Println(value.Kind()) // 输出: inttype S struct {
  a string}var s S
value2 := reflect.TypeOf(s) // 使用TypeOf()获取到结构体的Type对象fmt.Println(value2)        // 输出 main.Sfmt.Println(value2.Type()) // 输出:value2.Type undefined (TypeOf 就没有Type函数了)fmt.Println(value2.Kind()) // 输出:struct复制代码

struct

对于结构体的TypeOf,可以使用下面的方式对属性和函数进行遍历。

type S struct {
	A string
	B int `json: "b"`}func (t S) Mth(){	return}var s = S{"SSS", 1}
typ := reflect.TypeOf(s)for i := 0; i < typ.NumField(); i++ {
  f := typ.Field(i)
  fmt.Println(f.Name, f.Type)
}for i := 0; i < typ.NumMethod(); i++ {
  m := typ.Method(i)
  fmt.Println(m.Name, m.Func)
}复制代码

注意与Value的field相关函数进行区分:

reflect.Type 的field相关函数返回的是 reflect.StructField类型,它可以获取到字段名,字段类型

Field(i int) StructField

FieldByIndex(index []int) StructField

FieldByName(name string) (StructField, bool)复制代码

reflect.Value的field相关函数返回的是reflect.Value类型

func (v Value) Field(i int) Valuefunc (v Value) FieldByIndex(index []int) Value func (v Value) FieldByName(name string) Value复制代码

reflect.StructField有一点需要注意就是可以获取tag值

type S struct {
	A string
	B int `json:"tag"`}

s := S{"SSS", 1}

typ := reflect.TypeOf(s)
f, _ := typ.FieldByName("B")

fmt.Println(f.Tag.Get("json")) // 输出: tag复制代码


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

本文来自:51CTO博客

感谢作者:mb600beb5e8f23b

查看原文:go语言反射的使用

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

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