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复制代码
有疑问加站长微信联系(非本文作者)